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

开发 前端
我们给 super().__init__​ 方法传递了 (length, length) 参数,因为正方形的长宽相等。同时,我们可以调用父类计算面积和周长的方法,因为子类可以访问父类的方法。这样,不仅提升了代码的复用性,并且代码更简洁。

前路漫漫,我爱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}

如果我们希望调试和检查具有许多属性的复杂对象和类,这非常有用。

责任编辑:武晓燕 来源: 数据派探险家
相关推荐

2024-09-05 16:02:52

Python数据类型字符串

2021-04-14 09:22:18

Python技巧交换变量值

2019-08-09 14:13:22

Python知乎程序员

2017-06-01 09:34:55

R语言包sqldf

2021-09-28 14:40:03

Python内置库itertools

2024-08-20 14:25:20

2018-01-25 14:53:20

iPhone技巧删除照片

2021-08-20 15:43:54

iPhone手机iOS

2015-07-14 14:09:56

2020-05-06 09:18:56

Pandas函数大数据技术

2024-01-29 00:51:39

前端开发利器

2021-08-19 15:02:32

科技软件电脑

2020-05-07 09:02:55

代码Python开发

2020-08-23 18:18:27

Python列表数据结构

2024-01-23 18:49:38

SQL聚合函数数据分析

2016-12-09 12:50:36

Android

2023-09-20 22:52:12

Kubernetes快捷方式

2020-04-01 14:16:05

Pandastips数据分析

2019-07-05 07:49:19

TCPIP网络协议

2019-02-12 11:22:29

Linux 系统 命令
点赞
收藏

51CTO技术栈公众号