面向对象
约 1141 个字 28 行代码 预计阅读时间 4 分钟
面向过程当中的数据和函数是分离的,面向对象把数据和函数组织到了一起。Python中一切皆对象,包括内置int、str等,因为它们本质上是封装过的C语言指针
封装
封装指的是把数据和方法组织到同一个类中,Python的类中一般是首字母大写,每个类都有一个名为__init__的特殊方法,在创建实例时自动运行。类中的所有对象方法的第一个参数都是self(可以是其他标识符),表示当前对象本身,不过通过实例访问方法并不需要显式传递参数。
构造
__init__是一个特殊方法,它会在对象创建时自动调用- 可以用
isinstance()来判断一个对象是否为某个类的实例 - Python实现加减乘除是通过魔术方法
__add__、__sub__等
访问控制
Python的访问控制并不使用private和public关键字,Python无法实现真正意义上的private,Python约定:
_var:表示这是一个内部变量,不应该在外部访问,但其实还是可以访问,不能通过from module import *导入__var:Python 解释器会执行名称修饰,将这个名称转换为_类名__原始名称的形式。
类成员/方法
类成员很简单,类似于C++的静态成员,不过不需要关键字限定,只需要在所有方法之外定义,但是类方法就需要使用装饰器,类方法既可以通过类,也可以通过实例来调用:
- 类方法:使用
@classmethod,类方法不能访问实例变量,只能访问类成员,并且定义时第一个参数必须是对象本身cls,但是在调用时不需要也不能给该参数传参,因为事实上Python解释器会自动传入,在继承时会自动绑定给子类。 - 静态方法:使用
@staticmethod,静态方法通常是一个独立的方法,不对实例成员和类成员进行操作,在继承时不会绑定给子类,本质上是独立的函数,只是命名空间在类中。
Python作为一门动态类型的语言,可以在类创建之后添加新的成员,可以是给类添加,比如class.new_class_var = xxx,也可以是给实例添加,比如object.new_boject_var = xxx。
Python因为没有封装,所以实际上可以直接访问类的成员和方法,比如
访问成员
Python直接.就可以访问任意成员,此外可以用@property去修饰一个与成员变量同名的方法,来做到通过instance.get_method访问对应成员变量。@property 装饰器是一个内置的装饰器,用于将一个类的方法转换为属性进行访问,这个主要是可以做一些校验,也未必是真的赋值,比如你可以在赋值的时候就完成某些计算。
class MyClass:
def __init__(self):
self._value = None
@property
def value(self):
print("Calling getter...")
return self._value
@value.setter # 注意这里是 @value.setter,value 是上面 @property 方法的名字
def value(self, new_value):
print(f"Calling setter with {new_value}...")
if new_value < 0:
raise ValueError("Value cannot be negative")
self._value = new_value
obj = MyClass()
obj.value = 20 # 调用 setter 方法
print(obj.value)
try:
obj.value = -5 # 触发 setter 中的校验
except ValueError as e:
print(e)
继承
Python的继承使用class 类的名称(父类名称),如果参数为空,则默认继承自object,可以使用self调用自身的方法,supper调用父类的方法。Python中有个重要的概念是鸭子类型,因为Python是动态类型的语言,实际上不会检查类型到底是否正确,在鸭子类型中,一个对象的类型(它是什么类的实例)并不重要,重要的是它能做什么(它有什么方法和属性)。鸭子类型其实说明Python不仅仅是动态绑定,甚至哪怕没有继承关系的类,只要有对应的方法,也可以动态地绑定。
Python支持多继承,使用MRO算法查找,基本原则是从下往上找,优先选择最近的父类,如果有多个同级的无继承关系的父类,那么就按照定义的顺序,此外,子类的 MRO 不应该改变父类之间已建立的相对顺序。
多态
Python没有重载,不过子类覆写父类的方法还是可以的。Python如果定义同名的函数,后定义的会覆盖先前的,一个不是那么好的解决手段是默认参数,如果要真的重载可以考虑第三方库。Python许多运算符的重载是通过魔术方法,比如+实际会调用__add__,print实际上会优先调用__repr__,如果没有则尝试__str__。