【Python面向对象】封装机制的实现:类,及其语法特性

类,也许是从计算机的角度去结构这个世界的唯一方式。本文是一篇入门级别的文章,是有关于Python类的基础语法特性。主要内容有:类的方法(即C/C++中的函数),类的属性(即成员变量)。

写在前面:
本文是一篇学习笔记、有一定的总结性的文章,可能只适合有相关基础的人作为复习和查漏补缺。也许不适合新手!

如果你是新手,它可能适合你!

如果你是新手,它可能适合你!

类的定义

以Student类为例,在Python中,定义类是通过class关键字:

class Student(object):
	pass

解释:

  • class后面紧接着是类名,即Student,
  • 类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,
  • 通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

类方法

类方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。

三种类的方法

  1. 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self
  2. 类方法:由调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
  3. 静态方法:由调用;无默认参数;

三种类方法的调用:

class Foo:
    def __init__(self, name):
        self.name = name

    def ord_func(self):  # 定义普通方法,至少有一个self参数
        print('普通方法')

    @classmethod  # 定义类方法,至少有一个cls参数
    def class_func(cls):
        print('类方法')

    @staticmethod  # 定义静态方法 ,无默认参数
    def static_func():
        print('静态方法')

# 调用普通方法
f = Foo()
f.ord_func()

# 调用类方法
Foo.class_func()

# 调用静态方法
Foo.static_func()

三种方法的区别:

  • 相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
  • 不同点:方法调用者不同、调用方法时自动传入的参数不同。

特殊的内置方法

  1. __init__ 构造函数,在生成对象时调用
  2. __del__析构函数,释放对象时使用
  3. __str__ 显示该类的实例的信息,类似于Java中Object类的toString方法

为了不喧宾夺主,这里这是简单介绍一下内置方法,更加详细的内置方法,后文将更加详细地介绍!

init内置方法

__init__()

  • 类似于Java中的带参构造方法,特殊方法“init”前后分别有两个下划线!!!
  • 第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
  • self类似于Java中的this指针;
class Student(object): # 定义类
    def __init__(self, name, score): # 定义构造方法
		self.name = name
		self.score = score

del内置方法

__del__():
理解del内置方法:

  1. 与 init() 方法对应的是__del__() 方法,__init__() 方法用于初始化 Python 对象,
  2. __del__() 则用于销毁 Python 对象,
  3. 即在任何 Python 对象将要被系统回收之时,系统都会自动调用该对象的__del__() 方法。

当程序不再需要一个 Python 对象时,系统必须把该对象所占用的内存空间释放出来,这个过程被称为垃圾回收(GC,Garbage Collector),Python 会自动回收所有对象所占用的内存空间,因此开发者无须关心对象垃圾回收的过程

**注意:**对一个变量执行 del 操作,该变量所引用的对象就会被回收,只有当对象的引用计数变成 0 时,该对象才会被回收。因此,如果一个对象有多个变量引用它,那么 del 其中一个变量是不会回收该对象的。

class Item:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __del__ (self):  # 定义析构函数
        print('del删除对象')

im = Item('鼠标', 29.8)  # 创建一个Item对象,将之赋给im变量
x = im   # 打印im所引用的Item对象

del im
print('--这是最后一句Python语句--')  # 只有执行最后一句,该类的对象不再被使用,才del

'''输出:
--这是最后一句Python语句--
del删除对象
'''

str内置方法

__str__():
类似于Java中的“toString”方法;用于输出本类的信息;

class Student(object):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'Student object (name: %s)' % self.name


print(Student('Michael'))  #  输出:Student object (name: Michael)
# 如果不定义"__str__",输出:<__main__.Student object at 0x109afb190>

类属性

属性的定义有两种方式:

  1. 装饰器,即:在方法上应用装饰器
  2. 静态字段,即:在类中定义值为property对象的静态字段

在哪里定义类属性:
1、直接在类中定义属性

class UserInfo(object):
    name = '两点水'

2、在构造函数中定义属性

class UserInfo(object):
    def __init__(self, name):
        self.name = name

类变量的定义方式:

  1. xx公有变量
  2. _xx:单前置下划线,受保护的属性或方法(相当于protected),类对象和子类可以访问,from somemodule import *禁止导入
  3. __xx:双前置下划线,私有化属性或方法(相当于private),无法在外部直接访问(名字重整所以访问不到),访问这种变量只能通过getter和setter函数来实现。
  4. __xx__:双前后下划线,系统定义名字(不要自己发明这样的名字)
  5. xx_:单后置下划线,用于避免与Python关键词的冲突

静态变量

有两种方式:

  • 普通方式
  • property方式

普通静态变量

class Province:
    country = '中国'  # 类的静态变量,类似于Java中的static

    def __init__(self, name):
        self.name = name   # 普通(对象)的属性


obj = Province('河北省')  # 直接访问普通属性
print(obj.name)

print(Province.country)  # 直接访问静态变量

• 静态变量在内存中只保存一份
• 普通变量在每个对象中都要保存一份
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

property的静态变量

快速理解property方法:

class Foo:
    def get_bar(self):
        return 'constant info'
    BAR = property(get_bar)  # 创建常量"BAR",把get_bar方法的返回值赋值给它;

obj = Foo()
reuslt = obj.BAR    # 自动调用get_bar方法,并获取方法的返回值
print(reuslt)

property的构造方法中有个四个参数:

  • 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
  • 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
  • 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
  • 第四个参数是字符串,调用 对象.属性.doc ,此参数是该属性的描述信息
class Foo:
    def get_bar(self):
        return 'wupeiqi'
    def set_bar(self, value):  # *必须两个参数
        return
        return 'set value' + value
    def del_bar(self):
        return 'wupeiqi'
    BAR = property(get_bar, set_bar, del_bar, 'description...') # 定义一个BAR常量

obj = Foo()

obj.BAR  # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex"  # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
del Foo.BAR  # 自动调用第三个参数中定义的方法:del_bar方法
obj.BAE.__doc__  # 自动获取第四个参数中设置的值:description...

操作静态字段

  • 由于静态字段方式创建属性具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object):

    def __init__(self):
        self.original_price = 100  # 原价
        self.discount = 0.8  # 折扣

    def get_price(self):
        new_price = self.original_price * self.discount  # 实际价格 = 原价 * 折扣
        return new_price

    def set_price(self, value):
        self.original_price = value

    def del_price(self, value):
        del self.original_price

    PRICE = property(get_price, set_price, del_price, '价格属性描述...')


obj = Goods()
obj.PRICE         # 获取商品价格
print('现在商品的价格是:',obj.PRICE)
obj.PRICE = 200   # 修改商品原价
print('修改后商品的价格是:',obj.PRICE)
#del obj.PRICE     # 删除商品原价

装饰器

如果你已经了解Python类中的方法,那么属性就非常简单了,因为Python中的属性其实是普通方法的变种。即,通过“@property”装饰器,将一个方法变成一个变量或常量。

装饰器的类变量

class Goods:
    @property
    def price(self):
        return 'hahha'

obj = Goods()
result = obj.price  # 完全把price()方法,搞成了一个变量;如果result = obj.price()  错误!
print(result)  # 输出:hahha
# print(obj.price())  # 报错:TypeError: 'str' object is not callable

属性的定义和调用要注意一下几点:
• 定义时,在普通方法的基础上添加 @property 装饰器;
• 定义时,属性仅有一个self参数
• 调用时,无需括号,即被@property装饰的方法不再是一个方法,而是一个类属性(类变量);

注意: 属性存在意义是:访问属性时可以制造出和访问字段(类变量)完全相同的假象
属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。

装饰器与经典类、新式类

经典类(类名后的括号里没继承“object”):
# ############### 定义 ###############    
class Goods:
    @property
    def price(self):
        return "wupeiqi"
    
# ############### 调用 ###############

obj = Goods()
result = obj.price  # 使得price方法变成一个变量,并获取方法的返回值
print(result)

新式类(类名后的括号里有“object”):具有三种@property装饰器
# ############### 定义 ###############
# 新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法
class Goods(object):

    @property
    def price(self):
        print('@property')

    @price.setter
    def price(self, value):
        print('@price.setter')

    @price.deleter
    def price(self):
        print('@price.deleter')

# ############### 调用 ###############
obj = Goods()
obj.price          # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
obj.price = 123    # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数
del obj.price      # 自动执行 @price.deleter 修饰的 price 方法

核心点:

  • 经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
  • 新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法

新式类的三个装饰

class Goods(object):
    def __init__(self):
        self.original_price = 100  # 原价
        self.discount = 0.8   # 折扣

    @property
    def price(self):
        new_price = self.original_price * self.discount   # 实际价格 = 原价 * 折扣
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
obj.price         # 获取商品价格
print('原商品价格:',obj.price)  # 输出:原商品价格: 80.0

obj.price = 200   # 修改商品原价
print('修改后商品价格:',obj.price)  # 输出:修改后商品价格: 160.0

del obj.price     # 删除商品原价

内置变量:name

在有些Python文件中,在最后一行常见这样的代码:

`if __name__ == '__main__':`

这到底是什么意思呢?

  1. __name__是一个变量。前后加了双下划线是因为这是系统定义的名字。普通变量不要使用此方式命名变量。

  2. Python有很多模块,而这些模块是可以独立运行的!这点不像C++和C的头文件。

  3. import的时候是要执行所import的模块的。

  4. __name__就是标识模块的名字的一个系统变量。这里分两种情况:假如当前模块是主模块(也就是调用其他模块的模块),那么此模块名字就是__main__,通过if判断这样就可以执行“__mian__:”后面的主函数内容;假如此模块是被import的,则此模块名字为文件名字(不加后面的.py),通过if判断这样就会跳过“__mian__:”后面的内容。

通过上面方式,python就可以分清楚哪些是主函数,进入主函数执行;并且可以调用其他模块的各个函数等等。

  1. 如果模块是被导入,__name__的值为模块名字
  2. 如果模块是被直接执行,__name__的值为’__main__

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章