ITPub博客

首页 > 数据库 > 数据库开发技术 > Python必知必会:令人相见恨晚的10个Python类技巧(一)

Python必知必会:令人相见恨晚的10个Python类技巧(一)

原创 数据库开发技术 作者:jackzhang 时间:2024-08-22 06:30:00 0 删除 编辑

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

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

结论

好啦,关于Python类的10个技巧的分享就到这啦,还没完?别着急,关注我,我将在下一篇文章中跟大家分享剩余的5个技巧。

感谢你的阅读,希望这篇文章能对你有所帮助!


推荐阅读👇👇👇

5个良好的Python编程习惯,助你成为Python高手!

Python类型提示(type hints):提升代码质量与可读性的利器!墙裂推荐~~

Python编程技巧:在函数参数中请停止使用“x=[]”

9个令人相见恨晚的Python字典技巧,让你写出更优雅、更Pythonic的代码!

每天都在用的20个Python技巧,让你从此告别平庸!

- END -

🌟 如果你喜欢这篇文章并且愿意支持我的话,你可以:🌟

  • 👍👍👍 点赞文章
  • ⤴️⤴️⤴️ 转发文章
  • ⚛️⚛️⚛️ 点击在看
  • 🧑‍🤝‍🧑🧑‍🤝‍🧑🧑‍🤝‍🧑 让文章被推荐给更多人,找到更多志同道合的伙伴
  • 👏👏👏 欢迎给我留言

关注我,第一时间接收更多关于Python、数据分析和机器学习的深入分析和最新趋势。我们一起探索数据的力量,解锁AI的潜能。你的每一次互动都是对我的极大鼓励,也是我们共同成长的动力!

👇👇👇 关注并开启星标提醒,让知识与技能的火花不断在你的职业道路上燃烧吧!

原文作者:数据派探险家;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2017-12-19

  • 博文量
    119
  • 访问量
    55970