前路漫漫,我爱
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 类:
class Rectangle:
def __init__(self, length: float, width: float):
self.length = length
self.width = width
def area(self):
return self.length * self.width
def perimeter(self):
return 2 * (self.length + self.width)
if __name__ == '__main__':
r = Rectangle(length=10.5, width=6.4)
print(r.area()) # 67.2
print(r.perimeter()) # 33.8
接下来,我们定义 Square 类,它继承自 Rectangle 类。注意,为了尽可能复用现有的方法(来自父类),我们就会用到 super() 方法。
class Square(Rectangle):
def __init__(self, length: float):
super().__init__(length, length)
if __name__ == '__main__':
s = Square(5)
print(s.area()) # 25
print(s.perimeter()) # 20
这里,super() 指的是父类(即 Rectangle)。因此,super().__init__ 实际上就是在调用父类 Rectangle 的 __init__ 方法。
我们给 super().__init__ 方法传递了 (length, length) 参数,因为正方形的长宽相等。同时,我们可以调用父类计算面积和周长的方法,因为子类可以访问父类的方法。这样,不仅提升了代码的复用性,并且代码更简洁。
3. 实例方法 VS 类方法 VS 静态方法
实例方法(Instance methods)属于类(对象)的实例,它可以访问实例的属性。
例如,在下面的代码片段中,intro 就是 Dog 类的实例方法。
class Dog:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def introduce(self):
print(f'My name is {self.name}!')
if __name__ == '__main__':
rocky = Dog(name='Rocky', age=20)
rocky.introduce() # My name is Rocky!
类方法(Class methods)属于类(而不是实例),并且类方法只能访问类属性,而不能访问实例属性。
比如,在下面的示例中,get_employee_count 就是 Employee 类的一个类方法:
class Employee:
employee_count: int = 0
@classmethod
def get_employee_count(cls) -> int:
return cls.employee_count
def __init__(self, name: str, salary: int) -> None:
self.name = name
self.salary = salary
Employee.employee_count += 1
if __name__ == '__main__':
emp1 = Employee(name='John', salary=5000)
emp2 = Employee(name='Jack', salary=10000)
emp3 = Employee(name='Stefan', salary=8000)
print(Employee.get_employee_count()) # 3
关于类方法:
·我们使用 @classmethod 装饰器来表示定义类方法。
· 类方法接受的是 cls 参数而不是 self, cls 表示类本身(这里为 Employee)。
· 类方法(get_employee_count)只能访问类属性(如employee_count),而不能访问实例属性(如name, salary)。
静态方法(Static methods)属于类,它无法访问任何属性。
例如,在下面的示例中,description 就是 Employee 类的静态方法:
class Employee:
def __init__(self, name: str, salary: int) -> None:
self.name = name
self.salary = salary
@staticmethod
def description() -> str:
return 'Employees are the most basic and important resources for company.'
if __name__ == '__main__':
print(Employee.description())
# Employees are the most basic and important resources for company.
关于静态方法:
· 我们通过 @staticmethod 装饰器来定义静态方法。
· 请注意,静态方法不接受 cls 或 self 参数。
· 静态方法不能访问任何属性,包括类属性和实例属性。
4. 数据类(Dataclasses)
当我们需要创建具有许多简单属性的类时,使用数据类(dataclass)会尤其有用。比如,
from dataclasses import dataclass
@dataclass
class Employee:
name: str # 姓名
age: int # 年龄
gender: str # 性别
education: str # 学历
telphone: str # 电话
email: str # 邮箱
position: str # 职位
salary: int # 薪资
seniority: int # 工龄
def description(self) -> str:
return f"""The description of employee:
Name: {self.name}
Age: {self.age}
Gender: {self.gender}
Education: {self.education}
Telephone: {self.telphone}
Email: {self.email}
Position: {self.position}
Salary: {self.salary}
Seniority: {self.seniority}
"""
if __name__ == '__main__':
emp_info: list[str | int] = ['Jack', 29, 'Male', 'master', '188******666',
'jackzhang@example.com', 'manager', 10000, 5]
emp = Employee(*emp_info)
print(emp.description())
注意,我们不需要在数据类中编写 __init__ 方法,因为它已经为我们自动编写该方法。
此外,请注意,如果你需要在 __init__ 方法中执行一些特定的操作,那么可能使用数据类并不合适。
5.__dict__属性
当我们创建类的实例后,实际上实例对象的属性在底层存在一个特殊变量中(即__dict__),我们可以使用它来获取实例的属性信息。
class Employee:
def __init__(self, name: str, age: int) -> None:
self.name = name
self.age = age
if __name__ == '__main__':
emp = Employee(name="John", age=20)
print(emp.__dict__)
# {'name': 'John', 'age': 20}
即使是动态添加的属性,也可以通过对象的 __dict__ 属性获取:
class Employee:
def __init__(self, name: str, age: int) -> None:
self.name = name
self.age = age
if __name__ == '__main__':
emp = Employee(name="John", age=20)
print(emp.__dict__)
# {'name': 'John', 'age': 20}
emp.salary = 10000
print(emp.__dict__)
# {'name': 'John', 'age': 20, 'salary': 10000}
如果我们希望调试和检查具有许多属性的复杂对象和类,这非常有用。