本文整理自下面这个视频,推荐观看:
Object Oriented Programming (OOP) In Python - Beginner Crash Course
author:Python Engineer (Youtube)
前言
谈到OOP,就不得不谈继承、多态和封装。
继承实现了代码重用,并且是多态的基础;
多态提高了代码的灵活性、扩展性;
封装隐藏内部细节,更好地保护数据。
抽象是OOP的基础,有好的抽象能力才能设计出好的基类,好的函数层级…
(by 一只大鸽子)
1. Class & Instance
WHY?
为什么我们需要Class?
如果你的需求很简单,基本数据类型就可以满足,那么不需要Class。
但是随着需求变得复杂,你会发现有非常多的数据和函数需要管理。这个时候就需要用Class了。
举例:
你要写一个管理雇员信息的程序,现在正在写管理“软件工程师”人员的代码。
一个软件工程师有这些信息:职位、姓名、年龄、等级、工资。
你可以用列表来存储,或者用namedtuple
来存储信息。没问题,代码可以跑通。但是后面进行操作的时候,就会比较混乱。
因此,我们用类实现“软件工程师”:
在__init__
中初始化他的信息(属性)。
class SoftwareEngineer: def __init__(self, name, age, level, salary): self.name = name self.age = age self.level = level self.salary = salary if __name__ == '__main__': se1 = SoftwareEngineer('DD',20,'Senior', 7000)
class attributes & instance attributes
刚刚在__init__
中的属性是instance attributes
,
和每一个具体的软件工程师相关(如名字,每个软件工程师都有自己的名字),而class attributes
是和类相关,不依赖具体的人(如他们的中文名称都是“软件工程师”)。一般在class 中定义(而不在__init__
函数内)。
class SoftwareEngineer: # class attributes alias = "Keyboard Magician" # 别名 cn_name = "软件工程师" # instance attributes def __init__(self, name, age, level, salary): self.name = name self.age = age self.level = level self.salary = salary if __name__ == '__main__': se1 = SoftwareEngineer('DD', 20, 'Senior', 7000) print(se1.cn_name) print(SoftwareEngineer.cn_name)
2. Functions in Classes
现在我们要让软件工程师写代码了。
我们定义一个函数code()
,让某个工程师写代码:
def code(self): print(f"{self.name} is writing code...")
如果想让他用某个语言写代码,可以给函数加一个language
参数:
def code_in_language(self,language): print(f"{self.name} is writing code...in {language}")
dunder method
带有双下划线的方法,是Python中的一种特殊方法,也称为魔法方法,会被隐式地自动调用。
例如__init__方法,我们不用手动调用,而是创建对象时自动调用。se1 = SoftwareEngineer('DD', 20, 'Senior', 7000)
类中常用的一个dunder method 是__str__
,用来返回实例信息。
# dunder method def __str__(self): information = f"name = {self.name}, age={self.age}, level={self.level}" return information
__str__
方法返回一个包含实例信息的字符串,在print(实例)时会自动调用。
print(se1)
__eq__(self, other)
:比较两个对象相等时自动调用,该函数应该返回一个布尔类型值
类中的静态方法
使用@staticmethod
修饰。属于类但不属于某个具体的实例(没有self参数)。
@staticmethod def entry_salary(age): if age > 25 : return 7000 else: return 5000
3. Inheritance 继承
继承(inherits)、扩展(extend)、重写(override)
继承(inherits)
ChildClass(BaseClass)
继承一个类,意味着继承类的属性和方法。
class Employee: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary def work(self): print(f"f{self.name} is working...") class SoftwareEngineer(Employee): pass class Desiner(Employee): pass if __name__ == '__main__': se = SoftwareEngineer("DD",25,5000, "Junior") print(se.name)
扩展(extend)
新增方法。例如可以在SoftwareEngineer中新增debug方法。
class SoftwareEngineer(Employee): #override def __init__(self,name, age, salary, level): super().__init__(name,age,salary) self.level = level # extend def debug(self): print(f"{self.name} is debugging...")
重写(override)
和父类方法 同名时会覆盖掉父类方法,
注意:初始化方法__init__
重写时必须调用父类的初始化方法super().__init__
。
否则会出现意想不到的错误(找不到属性等问题)。
class Employee: def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary def work(self): print(f"{self.name} is working...") class SoftwareEngineer(Employee): #override def __init__(self,name, age, salary, level): super().__init__(name,age,salary) self.level = level # extend def debug(self): print(f"{self.name} is debugging...") # override def work(self): print(f"{self.name} is coding...") class Desiner(Employee): # override def work(self): print(f"{self.name} is designing...")
多态(Polymorphism)
调用同一方法,根据具体的类表现出不同的行为。
se = SoftwareEngineer("DD",25,5000, "Junior") de = Desiner("xd",25,5000) employes = [se,de] for employe in employes: employe.work()
4. Encapsulation(封装)
_salary
以单下划线开头的变量或函数(习惯上地、并非语法规定地)约定为私有变量,不该在外部直接访问或修改。
而应该通过getter和setter方法进行访问和修改。
__xx
以双下划线开头的变量会被重命名为_ClassName__xx
,避免意外访问。
虽然有这些约定,但是实际上Python没有实现真的私有变量。外部还是可以访问所有变量。
5. Properties(属性)
@property
修饰getter方法, 并且可以通过该函数名x获取属性x。函数名和属性名是一样的。
@x.setter
修饰setter方法,并且修改属性x时会自动调用该方法。
@x.deleter
修饰del 方法, 删除属性x时会自动调用,用的很少。
class SoftwareEngineer(): #override def __init__(self,name, age, salary,level): self.name = name self.age = age self._salary = salary self.level = level @property def salary(self): return self._salary @salary.setter def salary(self, value): self._salary = value @salary.deleter def salary(self): del self._salary if __name__ == '__main__': se1 = SoftwareEngineer('DD',25,5000,'Junior') print(se1.salary) se1.salary = 7000 print(se1.salary)