目录

Python学习之路-面向对象:类属性与类方法

简介

类是一个特殊的对象Python中一切皆对象:class AAA: 定义的类属于类对象,obj1 = AAA() 属于实例对象。除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法成为:类属性和类方法,通过类名.的方式可以访问类的属性或者调用类的方法。

提示
在程序运行时,类同样会被加载到内存,类对象在内存中只有一份,使用一个类可以创建出很多个对象实例

类属性

类属性就是给类对象中定义的属性,通常用来记录与这个类相关的特征。类属性不会用于记录具体对象的特征

属性的获取机制

Python 中属性的获取存在一个向上查找机制。因此,要访问类属性有两种方式:1.类名.类属性;2.对象.类属性(不推荐)

提示

向上查找机制:获取对象属性时,首先在对象内部查找对象属性,没有找到就会向上寻找类属性。

如果使用 对象.类属性 = 值 赋值语句,只会给对象添加一个属性,而不会影响到类属性的值

补充
类属性在内存中只保存一份。实例属性在每个对象中都要保存一份。通过类创建实例对象时,如果每个对象需要具有相同名字的属性,那么就使用类属性,用一份既可

类方法

类方法就是针对类对象定义的方法,在类方法内部可以直接访问类属性或者调用其他的类方法。

语法

1
2
3
@classmethod
def 类方法名(cls):
    pass

类方法需要用修饰器 @classmethod来标识,告诉解释器这是一个类方法类方法。类方法的第一个参数应该是cls。由哪一个类调用的方法,方法内的 cls 就是哪一个类的引用。这个参数和实例方法的第一个参数是 self 类似,通过类名. 调用类方法,调用方法时,不需要传递 cls 参数。在方法内部可以通过 cls. 访问类的属性,也可以通过 cls. 调用其他的类方法。

提示
使用其他名称也可以,不过习惯使用 cls

拓展

静态方法

当类中某个方法既不需要访问实例属性或者调用实例方法也不需要访问类属性或者调用类方法的时候可以把这个方法封装成一个静态方法。

语法

1
2
3
@staticmethod
def 静态方法名():
    pass

静态方法需要用修饰器@staticmethod来标识,告诉解释器这是一个静态方法。通过类名.调用静态方法。

补充

实例方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。

  • 实例方法:由对象调用;至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self;

  • 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;

  • 静态方法:由类调用;无默认参数;

  • 相同点:对于所有的方法而言,均属于类,所以 在内存中也只保存一份

  • 不同点:方法调用者不同、调用方法时自动传入的参数不同。

单例

单例设计模式

目的:让类创建的对象,在系统中只有唯一的一个实例,每一次执行 类名() 返回的对象,内存地址是相同的。

补充
设计模式是前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性

__new__方法

使用类名()创建对象时,Python 的解释器首先会 调用 __new__ 方法为对象分配空间。__new__方法是一个 由object 基类提供的内置的静态方法,主要作用有两个:

  1. 在内存中为对象分配空间

  2. 返回对象的引用

补充
Python 的解释器获得对象的引用后,将引用作为第一个参数,传递给 __init__ 方法

重写 __new__ 方法一定要return super().__new__(cls),否则 Python 的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法。

注意
__new__ 是一个静态方法,在调用时需要主动传递 cls 参数

Python 中的单例

  1. 定义一个类属性,初始值是 None,用于记录单例对象的引用
  2. 重写 __new__ 方法
  3. 如果类属性 is None,调用父类方法分配空间,并在类属性中记录结果
  4. 返回类属性中记录的对象引用。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Singleton(object):

    # 定义类属性记录单例对象引用
    instance = None

    def __new__(cls, *args, **kwargs):

        # 1. 判断类属性是否已经被赋值
        if cls.instance is None:
            cls.instance = super().__new__(cls)

        # 2. 返回类属性的单例引用
        return cls.instance
  1. 定义一个类属性 init_flag 标记是否执行过初始化动作,初始值为 False
  2. __init__ 方法中,判断 init_flag,如果为 False 就执行初始化动作
  3. 然后将 init_flag 设置为 True
  4. 这样,再次自动调用 __init__ 方法时,初始化动作就不会被再次执行了
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Singleton(object):

    # 记录第一个被创建对象的引用
    instance = None
    # 记录是否执行过初始化动作
    init_flag = False

    def __new__(cls, *args, **kwargs):

        # 1. 判断类属性是否是空对象
        if cls.instance is None:
            # 2. 调用父类的方法,为第一个对象分配空间
            cls.instance = super().__new__(cls)

        # 3. 返回类属性保存的对象引用
        return cls.instance

    def __init__(self):

        if not Singleton.init_flag:
            print("初始化单例")

            Singleton.init_flag = True


# 创建多个对象
singleton1 = Singleton()
print(singleton1)

singleton2 = Singleton()
print(singleton2)

property属性

定义

一种用起来像是使用的实例属性一样的特殊属性。property属性内部进行一系列的逻辑计算,最终将计算结果返回。

注意事项

  • 定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数

  • 调用时,无需括号

    1
    2
    
      方法:foo_obj.func()
      property属性foo_obj.prop
    

两种方式

  • 装饰器,即:在方法上应用装饰器
  • 类属性,即:在类中定义值为property对象的类属性

类属性访问方式

老式类中的属性只有一种访问方式,其对应被 @property 修饰的方法。新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除。

提示
当使用类属性的方式创建property属性时,经典类新式类无区别

参数

property方法中有个四个参数

  • 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
  • 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
  • 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
  • 第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息

魔法属性

  • __init__:初始化方法,通过类创建对象时,自动触发执行
  • __doc__:表示类的描述信息
  • __module__:表示当前操作的对象在那个模块
  • __class__:表示当前操作的对象的类是什么
  • __del__:当对象在内存中被释放时,自动触发执行
  • __call__:对象后面加括号,触发执行,即:对象() 或者 类()()
  • __dict__:类或对象中的所有属性(类的实例属性属于对象;类中的类属性和方法等属于类)
  • __str__:如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值
  • __getitem____setitem____delitem__:用于索引操作,如字典
  • __getslice____setslice____delslice__:用于切片操作,如列表

元类

元类就是用来创建类的“东西”,就是类的类。使用函数type对类查看类型是type类型,这是因为函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。

补充
type函数还有一种完全不同的功能,动态的创建类。type可以接受一个类的描述作为参数,然后返回一个类。