Python必知必会:令人相见恨晚的十个Python类技巧

上一篇 / 下一篇  2024-08-22 13:26:50

  前路漫漫,我爱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}
  如果我们希望调试和检查具有许多属性的复杂对象和类,这非常有用。

TAG: 软件开发 Python

 

评分:0

我来说两句

Open Toolbar