前路漫漫,我爱Python。Hello,大家好!今天笔者将向大家分享10个关于Python类的关键技巧,早点了解这些技巧有助于你写出更加优雅、高效和Pythonic的代码!
1. 继承(Inheritance)VS 组合(Composition)
有时候我们应该使用继承,有时应该使用组合。实际应用中应该如何选择呢?一起来看看。
继承应该用在 IS-A 关系中。比如,猴子是一种动物,圆是一种形状,小轿车是一种机动车。在Python面向对象编程中,可以像下面这样表达 IS-A 关系:
- Monkey 继承自 Animal 类
- Circle 类继承自 Shape 类
- Car 类继承自 Vehicle 类
图片
组合应该用在 HAS-A (或HAS-MANY) 关系中。比如,一只猴子有一个主人,一张轿车有一个引擎,一个公司有一个或多个员工。
相反,一只猴子不是一个主人,一张轿车不是一个引擎,一个公司不是一个员工。并且,从常识来看,这样表达也不合理。我们不能使用继承来表示这些关系——而是应该使用组合。
图片
请注意,此处并没有继承关系。相反,Monkey 对象包含了 Owner 对象,Car 对象包含了 Engine 对象,Company 对象包含了 Employee 对象。
对于继承和组合的选择需要格外谨慎,因为错误的选择可能会给你的项目带来意想不到的麻烦。
2. super()方法及其用途
super() —— 当用在类中时,它允许我们访问父类的方法。
假设我们有2个类:矩形(Rectangle)和正方形(Square)。我们都知道,正方形其实是一种特殊的矩形(即长宽相等)。因此,我们可以通过继承 Rectangle 类来创建 Square 类。这样,Rectangle 类是父类,而 Square 类则是子类。我们首先定义 Rectangle 类:
接下来,我们定义 Square 类,它继承自 Rectangle 类。注意,为了尽可能复用现有的方法(来自父类),我们就会用到 super() 方法。
这里,super() 指的是父类(即 Rectangle)。因此,super().__init__ 实际上就是在调用父类 Rectangle 的 __init__ 方法。
我们给 super().__init__ 方法传递了 (length, length) 参数,因为正方形的长宽相等。同时,我们可以调用父类计算面积和周长的方法,因为子类可以访问父类的方法。这样,不仅提升了代码的复用性,并且代码更简洁。
3. 实例方法 VS 类方法 VS 静态方法
实例方法(Instance methods)属于类(对象)的实例,它可以访问实例的属性。
例如,在下面的代码片段中,intro 就是 Dog 类的实例方法。
类方法(Class methods)属于类(而不是实例),并且类方法只能访问类属性,而不能访问实例属性。
比如,在下面的示例中,get_employee_count 就是 Employee 类的一个类方法:
关于类方法:
- 我们使用 @classmethod 装饰器来表示定义类方法。
- 类方法接受的是 cls 参数而不是 self, cls 表示类本身(这里为 Employee)。
- 类方法(get_employee_count)只能访问类属性(如employee_count),而不能访问实例属性(如name, salary)。
静态方法(Static methods)属于类,它无法访问任何属性。
例如,在下面的示例中,description 就是 Employee 类的静态方法:
关于静态方法:
- 我们通过 @staticmethod 装饰器来定义静态方法。
- 请注意,静态方法不接受 cls 或 self 参数。
- 静态方法不能访问任何属性,包括类属性和实例属性。
4. 数据类(Dataclasses)
当我们需要创建具有许多简单属性的类时,使用数据类(dataclass)会尤其有用。比如,
图片
注意,我们不需要在数据类中编写 __init__ 方法,因为它已经为我们自动编写该方法。
此外,请注意,如果你需要在 __init__ 方法中执行一些特定的操作,那么可能使用数据类并不合适。
5.__dict__属性
当我们创建类的实例后,实际上实例对象的属性在底层存在一个特殊变量中(即__dict__),我们可以使用它来获取实例的属性信息。
即使是动态添加的属性,也可以通过对象的 __dict__ 属性获取:
如果我们希望调试和检查具有许多属性的复杂对象和类,这非常有用。