Jusene's Blog

python 面向对象

字数统计: 2.8k阅读时长: 13 min
2018/06/04 Share

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
    @classmethod
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
    @staticmethod
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
    @property
5
    def number(self):
6
        return self.data
7
8
    @number.setter
9
    def number(self,n):
10
        self.data = n
11
12
    @number.deleter
13
    def number(self):
14
        print('can not remove')
1
>>> n = num()
2
>>> n.number
3
10
4
>>> n.number=20
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
    @wrap
    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
@print_name
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
    @catch_except
14
    def read_value(self):
15
        f=open('test.txt')
16
        return f.read()
17
18
    @catch_except
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
    @classmethod
9
    def public_class_method(cls):
10
        return 'public_class_method'
11
12
    @classmethod
13
    def __private_class_method(cls):
14
        return 'private_class_method'
15
16
    @staticmethod
17
    def public_static_method():
18
        return 'public_static_method'
19
20
    @staticmethod
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
    @classmethod
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
    @classmethod
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
    @classmethod
5
    def cls_print(cls):
6
        print('Subsub.cls_print')
7
8
    def foo(self):
9
    # 调用Base
10
        super(Sub,self).printed()
11
12
    @classmethod
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
@printed
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),多种实现 。

CATALOG
  1. 1. 封装
  2. 2. 继承
  3. 3. 多态