python 作为面向对象的语言,符合面向对象的三大特性:封装、继承和多态。
封装
- 首先基本的class
1 | class A: |
2 | def __init__(self,number): |
3 | self.number = number |
4 | def printd(self): |
5 | print(self.number) |
1 | >>> a = A(10) |
2 | >>> a.printed() |
3 | 10 |
4 | >>> a.number = 20 |
5 | >>> a.printed() |
6 | 20 |
以上是最基本的封装,一个类需要实例化方可被调用该类中定义的相应函数方法,在初始化过程中调用__init__
初始化,在这个调用函数方法的过程中。传入的第一个参数必须是实例化的对象,也就是我们的self,其实我们并不需要一定写self,只是我们都约定俗称都写self为实例话对象,cls为类对象。
- 直接使用类方法
1 | class A: |
2 | name = 'python' |
3 | def __init__(self): |
4 | self.number = 20 |
5 |
|
6 | def printed(cls): |
7 | print(cls.name) |
8 | print(cls().number) |
1 | >>> a = A() |
2 | >>> a.number |
3 | 20 |
4 | >>> a.printed() |
5 | python |
6 | 20 |
7 | >>> A.printed() |
8 | python |
9 | 20 |
不管这个方式是从实例调用还是从类调用,它都用第一个参数把类传递过来
- 静态方法
经常有一些类有关系的功能,但是在运行时又不需要实例和类参与的情况下用到静态方法,如更改环境或者修改其他类的属性等静态方法:
1 | key = 'ON' |
2 | def check(): |
3 | return (key == 'ON') |
4 | |
5 | class door: |
6 | def open(self): |
7 | if check(): |
8 | print('opening') |
9 | self.status = 'open' |
10 | def close(self): |
11 | if check(): |
12 | print('closing') |
13 | self.status = 'close' |
14 | def status(self): |
15 | if check(): |
16 | print('status') |
17 | return self.status |
1 | key='ON' |
2 | class door: |
3 |
|
4 | def check(): |
5 | return (key == 'ON') |
6 | def open(self): |
7 | if check(): |
8 | print('opening') |
9 | self.status = 'open' |
10 | def close(self): |
11 | if check(): |
12 | print('closing') |
13 | self.status = 'close' |
14 | def status(self): |
15 | if check(): |
16 | print('status') |
17 | return self.status |
18 | |
19 | d=door() |
20 | d.check() |
21 | True |
1 | key='ON' |
2 | class door: |
3 | def check(): |
4 | return (key == 'ON') |
5 | def open(self): |
6 | if check(): |
7 | print('opening') |
8 | self.status = 'open' |
9 | def close(self): |
10 | if check(): |
11 | print('closing') |
12 | self.status = 'close' |
13 | def status(self): |
14 | if check(): |
15 | print('status') |
16 | return self.status |
17 | |
18 | d=door() |
19 | d.check() |
20 | TypeError: check() takes 0 positional arguments but 1 was given |
- 访问控制
python类中访问控制通过私有属性控制,及__
开头,不以__
结尾
1 | class door: |
2 | def open(self): |
3 | self.__status = 'open' |
4 | def close(self): |
5 | self.__status = 'close' |
6 | def status(self): |
7 | return self.__status |
1 | >>> d = door() |
2 | >>> d.open() |
3 | >>> d.status() |
4 | 'open' |
5 | >>> vars(d) |
6 | {'_door__status': 'open'} |
7 | |
8 | # 进行修改 |
9 | >>> d.__status = ‘close’ |
10 | >>> d.__status |
11 | 'close' #修改成功了,感觉发现python的bug了 |
12 | >>> d.status() |
13 | 'open' # 而类的内部还是open的,这是就郁闷了 |
14 | |
15 | >>> vars(d) |
16 | {'__status': 'close', '_door__status': 'open'} #通过这里上面的变化就清楚了,我们并没有修改掉类的私有属性,而是新建了一个属性 |
17 | |
18 | # 所以基于上面的的现象,我们来试试修改内部的私有属性 |
19 | >>> d._door__status = 'close' |
20 | >>> d.status() |
21 | 'close' |
22 | |
23 | # 这里就是python的黑魔法,python的私有属性都是通过修改名称实现的,不到万不得已禁止这样修改私有属性,猴子布丁基于这个黑魔法实现 |
- 将函数方法定义为属性
1 | class num: |
2 | def __init__(self): |
3 | self.data = 10 |
4 |
|
5 | def number(self): |
6 | return self.data |
7 | |
8 |
|
9 | def number(self,n): |
10 | self.data = n |
11 | |
12 |
|
13 | def number(self): |
14 | print('can not remove') |
1 | n = num() |
2 | n.number |
3 | 10 |
4 | 20 n.number= |
5 | n.number |
6 | 20 |
7 | del n.number |
8 | can not remove |
装饰类
1
class wrap(object):
2
def __init__(self,fun):
3
self.__fun = fun
4
def __call__(self,num):
5
print('add 100')
6
print(num)
7
time.sleep(1)
8
return str(self.__fun(num)+100)
9
10
11
12
def hello(n):
13
return n
类装饰器
1 | def print_name(cls): |
2 | def get_name(self): |
3 | return cls.__name__ |
4 | cls.__name__ = get_name |
5 | return cls |
6 | |
7 |
|
8 | class H: |
9 | pass |
10 | |
11 | h=H() |
12 | h.__name__ |
13 | H |
- 装饰类中的方法
1 | def catch_except(func): |
2 | def wrap(*args,**kwargs): |
3 | try: |
4 | u = func(*args,**kwargs) |
5 | return u |
6 | except Exception as e: |
7 | print('exception {} raised, parse exception.'.format(e)) |
8 | return wrap |
9 | |
10 | class Test(object): |
11 | def __init__(self): |
12 | pass |
13 |
|
14 | def read_value(self): |
15 | f=open('test.txt') |
16 | return f.read() |
17 | |
18 |
|
19 | def get_value(self): |
20 | return 'hello world' |
21 | |
22 | t= Test() |
23 | t.read_value() |
24 | exception [Errno 2] No such file or directory: 'test.txt' raised, parse exception. |
25 | t.get_value() |
26 | 'hello world' |
继承
- 继承的基本写法
1 | class base: |
2 | def base_print(self): |
3 | print('base') |
4 | |
5 | class A(base): |
6 | def a_print(self): |
7 | print('a') |
8 | |
9 | a=A() |
10 | a.a_print() |
11 | a |
12 | a.base_print() |
13 | base |
继承最明显的一个好处就是可以获取父类的属性和方法,那么哪些属性与方法可以被继承:
1 | class Base: |
2 | PUBLIC_CLASS_VAR = 'PUBLIC_CLASS_VAR' |
3 | __PRIVATE_CLASS_VAR = 'PRIVATE_CLASS_VAR' |
4 | def __init__(self): |
5 | self.public_instance_var = 'public_instance_var' |
6 | self.__private_instance_var = 'private_instance_var' |
7 | |
8 |
|
9 | def public_class_method(cls): |
10 | return 'public_class_method' |
11 | |
12 |
|
13 | def __private_class_method(cls): |
14 | return 'private_class_method' |
15 | |
16 |
|
17 | def public_static_method(): |
18 | return 'public_static_method' |
19 | |
20 |
|
21 | def __private_static_method(): |
22 | return 'private_static_method' |
23 | |
24 | def public_instance_method(self): |
25 | return 'public_instance_method' |
26 | |
27 | def __private_instance_method(self): |
28 | return 'private_instance_method' |
继承测试:
1 | class Sub(Base): |
2 | def printed(self): |
3 | print(self.PUBLIC_CLASS_VAR) |
4 | s=Sub() |
5 | s.printed() |
6 | PUBLIC_CLASS_VAR |
7 | |
8 | class Sub(Base): |
9 | def printed(self): |
10 | print(self.__PRIVATE_CLASS_VAR) |
11 | s=Sub() |
12 | s.printed() |
13 | AttributeError: 'Sub' object has no attribute '_Sub__PRIVATE_CLASS_VAR' |
14 | |
15 | vars(s) ## 根据封装那时的私有属性,可以猜到继承一样使用的更换变量名来实现的 |
16 | {'_Base__private_instance_var': 'private_instance_var', |
17 | 'public_instance_var': 'public_instance_var'} |
按照自己一个个方法试下去,基本可以得出以下结论:
凡是公有的都可以被继承
凡是私有的都不可以被继承
原来是什么,继承过来还是什么
方法重写
1 | class Base: |
2 | def printed(self): |
3 | print('Base.print') |
4 |
|
5 | def cls_foo(cls): |
6 | print('Base.cls_print') |
7 | |
8 | class Sub(Base): |
9 | def printed(self): # 当子类和父类有同名成员的时候, 子类的方法会覆盖父类的同名成员 |
10 | print('Sub.print') |
11 | |
12 | sub=Sub() |
13 | sub.printed() |
14 | Sub.print |
这里会出现我们就无法获取父类的方法,如果我们需要在适当的时候我们需要父类的方法:
1 | class Sub(Base): |
2 | def printed(self): |
3 | print('Sub.print') |
4 | def foo(self): |
5 | super().printed() # 调用父类的方法,等价于 super(Sub,self).printed() |
6 | super(Sub,self).printed() |
7 |
|
8 | def cls_foo(cls): |
9 | super().cls_foo() |
10 | |
11 | s=Sub() |
12 | s.foo() |
13 | Base.print |
14 | Base.print |
15 | s.cls_foo() |
16 | Base.cls_print |
多级继承:
1 | class Subsub(Sub): |
2 | def printed(self): |
3 | print('Subsub.print') |
4 |
|
5 | def cls_print(cls): |
6 | print('Subsub.cls_print') |
7 | |
8 | def foo(self): |
9 | # 调用Base |
10 | super(Sub,self).printed() |
11 | |
12 |
|
13 | def cls_foo(cls): |
14 | # Base.cls_print() |
15 | super(Sub,cls).cls_foo() #继承Base类,第一个参数 指定调用谁的父类 第二个参数指定调用是 传递第一参数 |
16 | super(Subsub,cls).cls_foo() #继承Sub类 |
17 | super().cls_foo() #继承Sub类 |
18 | |
19 | su = Subsub() |
20 | su.printed() |
21 | Subsub.print |
22 | su.foo() |
23 | Base.print |
24 | su.cls_foo() |
25 | Base.cls_print |
26 | Base.cls_print |
当父类含有一个参数的初始化方法的时候,子类一定需要一个初始化方法,并且在初始化方法的时候调用父类的初始化方法
1 | class Base: |
2 | def __init__(self,a,b): |
3 | self.__a = a |
4 | self.__b = b |
5 | def sum(self): |
6 | return self.__a+self.__b |
7 | |
8 | class Sub(Base): |
9 | def __init__(self,a,b) |
10 | super(Sub,self).__init__(a,b) |
super只能获取类的属性
1 | class Base: |
2 | NAME = 'BASE' |
3 | |
4 | class Sub(Base): |
5 | NAME = "SUB" |
6 | def print(self): |
7 | print(Sub.NAME) |
8 | print(super().NAME) |
9 | print(super(Sub,Sub).NAME) |
10 | |
11 | s = Sub() |
12 | s.print() |
13 | SUB |
14 | BASE |
15 | BASE |
- 多继承
MRO: 方法查找顺序
- 本地优先: 自己定义或重写的方法优先,按照继承列表,从左向右查找
- 单调性: 所以子类,也要满足查找顺序
1 | class A: |
2 | def print(self): |
3 | print('method of A') |
4 | |
5 | class B: |
6 | def print(self): |
7 | print('method of B') |
8 | |
9 | class C(A,B): |
10 | pass |
11 | |
12 | c = C() |
13 | c.print() |
14 | method of A |
15 | |
16 | class D(A): |
17 | def print(self): |
18 | print('method of D') |
19 | |
20 | class E(A,D): |
21 | pass |
22 | Cannot create a consistent method resolution order (MRO) for bases A, D |
23 | |
24 | class F(D,A): |
25 | pass |
26 | |
27 | f = F() |
28 | f.print() |
29 | method of D |
30 | |
31 | A.__mro__ |
32 | (__main__.A, object) |
33 | |
34 | D.__mro__ |
35 | (__main__.D, __main__.A, object) |
36 | |
37 | F.__mro__ |
38 | (__main__.F, __main__.D, __main__.A, object) |
39 | |
40 | # 如果E可以定义成功 |
41 | E.__mro__ |
42 | (__main__.E, __main__.A, __main__.D, object) #所有子类也需要满足查找顺序 |
C3算法保障mro的两个原则:
1 | class B(O) -> [B,O] |
2 | |
3 | class B(A1, A2, A3 ...An) -> [B] + merge(mro(A1),mro(A2),...mro(An),[A1,A2,...An, O]) |
merge的步骤:
遍厉列表
看第一个列表的首元素
- 它在其他列表中也是首元素
- 他在其他列表中不存在
满足以上两种情况,移除,合并到MRO
不满足,抛出异常
1
mro(G) -> [G] + merge(mro(E),mro(A),[E,A,O])
2
-> [G] + merge([E,A,O],[A,O],[E,A,O])
3
-> [G,E] + merge([A,O],[A,O],[A,O])
4
-> [G,E,A] + merge([0],[0],[0])
5
-> [G,E,A,O]
多继承的应用Mixin
1 | class Document: |
2 | def __init__(self,content): |
3 | self.content = content |
4 | |
5 | class Word(Document): |
6 | def __init__(self,content): |
7 | super().__init__('word: {}'.format(content)) |
8 | |
9 | class Excel(Document): |
10 | def __init__(self,content): |
11 | super().__init__('excel: {}'.format(content)) |
12 | |
13 | ## 类装饰器 |
14 | def printed(cls): |
15 | def _print(self): |
16 | print(self.content) |
17 | cls.print = _print |
18 | return cls |
19 | |
20 |
|
21 | class PrintableWord(Word): |
22 | def __init__(self,content): |
23 | super().__init__(content) |
24 | |
25 | p = PrintableWord('jusene') |
26 | p.print() |
27 | word: jusene |
28 | |
29 | ## Minin |
30 | class PrintableMixin: |
31 | def print(self): |
32 | print(self.content) |
33 | return self.content |
34 | class PrintableWod(PrintableMixin, Word): |
35 | pass |
36 | |
37 | p = PrintableWord('jusene') |
38 | p.print() |
39 | word: jusene |
40 | |
41 | class PrintableMonitor(PrintableMixin): |
42 | def print(self): |
43 | print(super().print()) |
44 | print('Monitor: {}'.format(super().print())) |
45 | |
46 | class PrintableWord(PrintableMonitor,Word): |
47 | pass |
48 | |
49 | p = PrintableWord('jusene') |
50 | p.print() |
51 | word: jusene |
52 | Monitor: word: jusene |
Minxin类应该遵守的标准:
- Mixin类不应该有初始化方法
- Mixin类通常不能独立工作
- Mixin类父类也应该是Mixin类
- 广度优先 深度优先
python 2.2之前类没有共同的祖先,之后引入object类,python2为了兼容分为经典类和新式类,python3中的全部都是新式类,新式类可以使用super。
经典类之深度优先: 经典类的搜索方式是按照“从左至右,深度优先”的方式去查找属性
1 | class P1: |
2 | def foo(self): |
3 | print('pi-foo') |
4 | |
5 | class P2: |
6 | def foo(self): |
7 | print('p2-foo') |
8 | |
9 | def bar(self): |
10 | print('p2-bar') |
11 | |
12 | class C1(P1,P2): |
13 | pass |
14 | |
15 | class C2(P1,P2): |
16 | def bar(self): |
17 | print('C2-bar') |
18 | |
19 | class D(C1,C2): |
20 | pass |
21 | |
22 | d=D() |
23 | d.foo() # D -> C1 -> P1 |
24 | p1-foo |
25 | d.bar() # D -> C1 -> P1 -> P2 |
26 | p2-bar |
新式类之广度优先:
1 | class P1(object): |
2 | def foo(self): |
3 | print('pi-foo') |
4 | |
5 | class P2(object): |
6 | def foo(self): |
7 | print('p2-foo') |
8 | |
9 | def bar(self): |
10 | print('p2-bar') |
11 | |
12 | class C1(P1,P2): |
13 | pass |
14 | |
15 | class C2(P1,P2): |
16 | def bar(self): |
17 | print('C2-bar') |
18 | |
19 | class D(C1,C2): |
20 | pass |
21 | |
22 | d=D() |
23 | d.foo() # D -> C1 -> C2 -> P1 |
24 | p1-foo |
25 | d.bar() # D -> C1 -> C2 |
26 | c2-bar |
多态
同一种事物的多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
1 | class People: |
2 | def run(self): |
3 | raise AttributeError('子类来实现') |
4 | |
5 | class Man(Peoele): |
6 | def run(self): |
7 | print('A Man') |
8 | |
9 | class Woman(People): |
10 | def run(self): |
11 | print('A Woman') |
12 | |
13 | people = People() |
14 | man = Man() |
15 | woman = Woman() |
16 | |
17 | people.run() |
18 | man.run() |
19 | woman.run() |
- 多态性
多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
1 | def func(cls): |
2 | cls.run() |
3 | |
4 | func(people) |
5 | func(man) |
6 | func(woman) |
多态性是一个接口(函数func),多种实现 。