01. 类属性

1.1 类属性与对象属性的概念

  • 对象属性:
    • 就是在类的__init__()方法中使用self.属性名 = 属性值的方式给类的对象添加的属性。
    • 对象属性存储在内存的堆中,同一个类的不同对象之间,属性数据相互独立不共享。
  • 类属性:

    • 是指直接定义在类中属性。类属性跟随类的定义直接加载到方法区的静态池中。
    • 一个类的所有对象共享同一份类属性数据。

      1.2 类属性的定义

      1.2.1 以对象属性引入

  • 定义一个Dog类,要求有品种、年龄、性别、所属科目四个属性。定义完成后,实例化四个对象。 ```python class Dog: def init(self, variety, age, gender, type):

    1. # 这里的属性都是对象属性
    2. self.variety = variety # 品种
    3. self.age = age # 年龄
    4. self.gender = gender # 性别
    5. self.type = type # 所属科目

d1 = Dog(“哈士奇”, 2, “雄”, “犬科”) d2 = Dog(“边牧”, 5, “雌”, “犬科”) d3 = Dog(“拉布拉多”, 3, “雌”, “犬科”) d4 = Dog(“金毛”, 1, “雄”, “犬科”)

  1. - 此时,Dog中所有的属性都是对象属性,都存储在堆中并且相互之间是独立的。
  2. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/2692415/1662279947510-c367c7dd-ba23-4215-a97c-4fa6596d0235.png#averageHue=%23a6917f&clientId=u4c4f8de9-f970-4&from=paste&height=491&id=u3884240c&originHeight=1040&originWidth=1059&originalType=binary&ratio=1&rotation=0&showTitle=false&size=84286&status=done&style=none&taskId=u8d271dab-1c57-41ba-a4d8-4ee20952527&title=&width=500)
  3. <a name="a0eWV"></a>
  4. #### 1.2.2 使用类属性描述共享的数据
  5. - 1.2.1的程序中,我们发现其实Dog类的所有对象的所属科目都应该是犬科的,因为狗就是属于犬科的。
  6. - 使用对象属性可以表述所属科目这个信息,但是所有的对象都会有一份,数据还是完全相同的,这实际上是浪费了内存空间出现数据冗余的体现。
  7. - 因此,可以将Dog类的type属性定义为类属性
  8. ```python
  9. class Dog:
  10. type = "犬科" # 所属科目,类属性
  11. # 此时在__init__()方法中不需要再定义所属科目。
  12. def __init__(self, variety, age, gender):
  13. self.variety = variety # 品种
  14. self.age = age # 年龄
  15. self.gender = gender # 性别
  16. d1 = Dog("哈士奇", 2, "熊")
  17. d2 = Dog("边牧", 5, "雌")
  18. d3 = Dog("拉布拉多", 3, "雌")
  19. d4 = Dog("金毛", 1, "熊")
  20. # Dog类的所有对象都共享唯一的type属性数据。
  21. print(d1.type, d2.type, d3.type, d4.type) # 犬科 犬科 犬科 犬科
  • 此时,在方法区的静态池中就出现了一个Dog.type = "犬科"的数据,Dog类的所有对象都共享这个Dog.type,以此节约了内存空间。

image.png

  • 注意:类属性不仅可以通过类的对象调用,还可以直接通过类调用。

    1. print(d1.type) # 犬科
    2. print(Dog.type) # 犬科

    1.3 类属性只能通过类修改

  • 调用类属性时可以通过类.类属性或者类的对象.类属性来调用,但是要修改类属性时只能通过类.类属性 = 新值的方式修改。

    1. Dog.type = "猫科"
    2. print(d1.type, d2.type, d3.type, d4.type) # 猫科 猫科 猫科 猫科
  • 注意:对象.类属性 = 新值在语法上并不报错,但是这种方式只会修改对象本身属性值,对类的其他对象并没有影响。

    • 本质上就是说在对象中创建了一个与类属性同名的对象属性,该对象属性的值覆盖了类属性的值(在堆中动态增加了属性,通过__dict__可查看)。
    • 但实际方法区静态池中的类属性的值并没有被修改。 ```python d1 = Dog(“哈士奇”, 2, “熊”) d1.type = “猫科” print(d1.dict) # {‘variety’: ‘哈士奇’, ‘age’: 2, ‘gender’: ‘熊’, ‘type’: ‘猫科’},发现d1中增加了type属性,且值为“猫科”。

d2 = Dog(“边牧”, 5, “雌”) print(d2.dict) # {‘variety’: ‘边牧’, ‘age’: 5, ‘gender’: ‘雌’},而d2中没有。

d3 = Dog(“拉布拉多”, 3, “雌”) d4 = Dog(“金毛”, 1, “熊”)

print(d1.type, d2.type, d3.type, d4.type) # 猫科 犬科 犬科 犬科 print(Dog.type) # 犬科

  1. <a name="LAJm0"></a>
  2. ## 02. 类方法
  3. <a name="nMHJC"></a>
  4. ### 2.1 类方法与对象方法的概念
  5. - 对象方法:
  6. - 对象方法是指定义在类中,并且方法的首个参数是self的方法。
  7. - 当通过对象调用对象方法时,Python解释器会自动将调用该方法的对象的地址赋值给self。
  8. - 当通过类来调用对象方式时,解释器不会自动给self赋值。
  9. - 类方法:
  10. - 类方法是指被`@classmethod`装饰器修饰的方法,并且类方法的第一个参数为cls,用来接收当前类的地址。
  11. - 类方法可以直接被类调用,也可以被类的对象调用,这两种调用方式Python解释器都会自动给cls赋值。
  12. - 类方法的使用场景:当函数中某个方法要来操作类属性时,建议将这个方法设置为类方法。
  13. <a name="BytXh"></a>
  14. ### 2.2 类方法的应用示例
  15. - 应用需求:
  16. - 定义一个Person类,有一个name属性。
  17. - 要求实例化对象后,可以查看到该Person共实例化了多少个对象。
  18. - 实现思路:
  19. - 定义一个类变量来记录Person类实例化对象的个数,因为这个属性不能随意的被普通对象修改,因此该属性应该被私有化。
  20. - 定义一个方法来完成计数操作,当对象被初始化时,就调用该方法进行计数。
  21. - 代码实现:
  22. ```python
  23. class Person:
  24. __count = 0 # 定义类属性,记录当前类实例化对象的个数。
  25. def __init__(self, name):
  26. self.name = name
  27. self.counter() # 实例化完成后,调用计数器进行计数。
  28. @classmethod
  29. def counter(cls):
  30. cls.__count += 1 # 类属性需要用cls(当前类)来访问修改。
  31. @classmethod
  32. def get_count(cls):
  33. return cls.__count
  34. p1 = Person("小明")
  35. print(f"Person类已经实例化了{Person.get_count()}个对象") # Person类已经实例化了1个对象
  36. p2 = Person("小张")
  37. print(f"Person类已经实例化了{Person.get_count()}个对象") # Person类已经实例化了2个对象