Object Oriented Programming
简称 OOP,一种程序设计思想,以对象为程序基本单元,一个对象包含数据和数据的操作方法;
面向对象的设计思想来自自然界的类(Class)和实例(Instance),抽象出 Class
,根据 Class
创建 Instance
,抽象程度比函数高(既包含数据,又包含操作数据的函数);
数据封装、继承、多态是面向对象的三大特点;
Class
是抽象的模板,是创建 Instance
的模板;
>>> class Student(object): # 定义类
... pass
...
>>> s1 = Student() # 创建实例
>>> s1.name = 'Aurelius' # 实例属性绑定
>>> s1.name
'Aurelius'
Python 允许对实例绑定任何数据,即使一个类的两个实例,他们拥有的变量名称都可能不同;
创建实例时强制绑定属性;
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
和普通函数相比,类中定义的函数第一个参数永远是实例变量 self
,且调用时不用传值,self
指向创建的实例本身
数据封装
通过实例函数访问实例的数据,不直接从外部访问这些数据;
限制外部代码对实例内部一些属性和方法的访问,可以加强代码的健壮性;
Python 没有任何强制机制限制访问权限;
在 Class 定义的内部属性前加 __
可以把属性变成私有,通过 get_p(), set_p() 方法来访问这些属性;
__property
被 Python 解释器自动改为 _ClassName__property
;
__xxx__
在 Python 中属于特殊属性,不是私有的,可以被访问的;
继承
,可以把父类的所有功能直接拿来,子类只需要新增自己特有的方法,重写覆盖父类中不适合的方法;
多态
,是把一个子类对象赋给一个父类变量,调用方法是子类实现;
开闭原则
对扩展开放,对修改封闭;
鸭子模式
def twice_run(animal):
animal.run()
animal.run()
class Timer(object):
def run(self):
print('Start ...')
传入 twice_run() 的对象不必是 Animal 的子类,只要有 run() 即可;
因此相对静态语言,动态语言(Python)的继承不是那么必要;
获取对象类型;
类型 | 类型常量 | 对象 |
---|---|---|
int | int | 123 |
str | str | ‘123’ |
函数 | types.FunctionType | def fn(): pass |
内建函数 | types.BuiltinFunctionType | abs |
匿名函数 | types.LambdaType | lambda x: x |
生成器 | types.GeneratorType | (x for x in range(10)) |
判断是否指定类型或指定类型元组中的一个;
>>> isinstance(x, t) # x 是对象,t 是类型,t 可以是多个类型组成的一个 tuple 对象
获取对象的所有属性和方法;
len()
实际是调用了对象的 __len__() 方法,因此按照鸭子类型,自己的类只要实现了 __len__(),也可以使用 len(myObj);
getattr(obj, pname)
获取对象的指定属性或方法,也可以传第 3 个参数作为默认值;
setattr(obj, pname, pvalue)
设置对象指定属性的值;
hasattr(obj, pname)
判断对象是否存在指定名称的属性或方法;
可以确定存在而直接访问的属性或方法,就不要使用getattr
,因此可以通过hasattr
判断属性或方法是否存在;
通过实例变量或self
变量绑定实例属性
;
通过类本身绑定的是类属性
,类属性归类所有,但类的所有实例都可以访问到;
>>> class Student(object):
... name = 'Student'
...
>>> s = Student()
>>> print(s.name)
Student
>>> print(Student.name)
Student
>>> s.name = 'Aurelius'
>>> print(s.name)
Aurelius
>>> print(Student.name)
Student
>>> del s.name
>>> print(s.name)
Student
相同名称的实例属性将屏蔽类属性(当查找到对应名称的实例属性时,即使存在同名类属性,也不会被查找到),当删除实例属性后,可以再次访问到类属性;
动态绑定方法
给Instance
绑定方法,只对当前Instance
有效;
class Student(object):
pass
s = Student()
def set_age(self, age):
self.age = age
from types import MethodType
s.set_age = MethodType(set_age, s)
s.set_age(25)
给Class
绑定方法,对所有Instance
有效;
def set_score(self, score):
self.score = score
Student.set_score = set_score
s.set_score(100)
使用 __slots__
用来限制 class
实例可以添加的属性,包括Class
定义中绑定的属性;
class Student(object):
# tuple定义允许绑定的属性名称
__slots__ = ('name', 'age')
__slots__
只对当前类有效,对子类无效;若子类也定义了__slots__
,有效范围是自身加父类的范围;
不定义__slots__
的类相当于其有效范围是任意属性;
把一个getter
方法变成属性,同时创建另一个装饰器@pname.setter
,负责把另一个setter
方法变成属性赋值;
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
# 只读属性
@property
def age(self):
return 2020 - self._birth
Python 允许使用多重继承,一个子类可以同时继承多个父类的所有功能;
MixIn
除了继承自主线,还额外混入其他类的功能,这种设计叫MixIn
;
通过一些__xxx__
属性定制类;
__str__
返回给用户看到的字符串,实例的打印结果;
__repr__
返回实例调式值显示结果;
__iter__
将Class
实例变成一个迭代器,需要实现__next__()
方法,for 循环会不断调用迭代对象的__next__()
;
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
if self.a > 1000:
raise StopIteration()
return self.a
__getitem__
通过索引器或者切片读取实例的值时被调用;
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice):
start, stop = n.start, n.stop
if start is None:
start = 0
a, b = 1, 1
res = []
for x in range(stop):
if x >= start:
res.append(a)
a, b = b, a + b
return res
slice
的step
参数和负数值可以进一步处理
__getattr__
当调用实例不存在的属性时被调用,已有的属性不会在__getattr__
中查找;
__getattr__
可以实现完全动态的调用
class Chain(object):
def __init__(self, path=''):
self._path = path
def __getattr__(self, path):
return Chain(f'{self._path}/{path}')
def __call__(self, param):
return Chain(f'{self._path}/{param}')
def __str__(self):
return self._path
__repr__ = __str__
print(Chain().status.user('Aurelius').timeline.list)
__call__
定义了__call__
,就可以调用实例本身,__call__
可以有参数;
class Student(object):
def __init__(self, name):
return self._name
def __call__(self, text):
print(f'{self._name}: {text}')
print(Student('Aurelius')('A'))
类本身的调用会执行type
的__call__
方法
将一组相关常量定义在一个Class
中,并且不可变,成员可以直接比较;
通过Enum
调用
>>> from enum import Enum
>>> Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
>>> for name, member in Month.__members__.items():
... print(name, '=>', member, ',', member.value)
...
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12
通过继承Enum
from enum import Enum, unique
@unique
class Weekday(Enum):
Sum = 0 # 默认从 1 开始,这里设置 0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
获取方式
Enum['Name']
Enum.Name
Enum(value)
type
type
函数既可以返回一个对象的类型,又可以创建出新的类型;
type
创建class
def fn(self, name='world'):
print(f'Hello, {name}')
Hello = type('Hello', (object,), dict(hello=fn))
h = Hello()
h.hello()
type()
的 3 个参数:
metaclass
metaclass
允许创建类或修改类,可以把类看作metaclass
创建的实例;
定义metaclass
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
__new__()
的 4 个参数依次是:
定制类
# 在 Python 解释器创建 MyList 时,要通过 ListMetaclass.__new__() 来创建
class MyList(list, metaclass=ListMetaclass):
pass
Python 解释器首先在当前类的定义中查找metaclass
,如果没有,就继续在父类查找,知道找到,用来创建当前类,metaclass
隐式继承到子类;
上一篇:「Python 基础」函数与高阶函数
专栏:《Python 基础》
PS:感谢每一位志同道合者的阅读,欢迎关注、评论、赞!