Python 面向对象

作为高级面向对象语言,Python 非常的灵活。

__slots__

定义一个学生对象,

1
2
class Student(object):
pass

一般语言,都是在初始化对象的时候,先定义好对象的属性和方法。

而 Python 可以在程序运行的过程中,动态的添加属性和方法。

实例动态添加属性:

1
2
3
4
5
6
7
8
class Student(object):
pass

stu = Student()

stu.name = 'zlf'

print(stu.name)

非常 easy 。

实例动态添加方法:

1
2
3
4
5
6
7
8
9
10
11
12
class Student(object):
pass
stu = Student()

def set_name(self,name):
self.name = name

from types import MethodType
stu.set_name = MethodType(set_name,stu)
stu.set_name('zlf')

print(stu.name)

注意两点MethodType() 方法传入两个参数,第一个是方法,第二个是对象,还有实例动态添加属性,加的只是该实例的属性,不是对象的属性。

对象动态添加方法:

1
2
3
4
5
6
7
8
9
10
11
12
class Student(object):
pass

def set_name(self,name):
self.name = name

Student.set_name = set_name

stu = Student()
stu.set_name('zlf')

print(stu.name)

对象添加的属性,所有实例都可以使用。

Java 使用者表示,这也太灵活了吧,难道都随便添加属性和方法吗? 不,用 __slots__ 这个特殊的变量就具有限制的作用。

slot 这个单词有硬币投币口的意思。

用法:

1
2
3
4
5
6
class Student(object):
__slots__ = ('name','age')

stu = Student()
stu.name = 'zlf'
stu.city = 'hangz'

很明显,没有出现在 slots 白名单的属性是不允许被添加的:

1
2
3
4
Traceback (most recent call last):
File "/Users/zhanglf/Documents/Python/practice/HelloWorld.py", line 9, in <module>
stu.city = 'hangz'
AttributeError: 'Student' object has no attribute 'city'

注意一点:这个白名单默认是不被继承的,当子类也定义白名单的时候,父类白名单才会被激活。

@property

正常情况下我们的 get() set() 是这样写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python
# -*- coding: utf-8 -*-

class Student(object):

def get_age(self):
return self.age

def set_age(self,value):
if not isinstance(value, int):
raise ValueError('age must be an integer!')
if value < 0 or value > 120:
raise ValueError('age must be 0 ~ 100!')
self.age = value

stu = Student()
stu.set_age(50)
print stu.get_age()

运行结果当然是: 50 。

但是调用的过程对于 Python 来说不够优雅,于是有了神奇的 @property 。

它的用处就是将调用 set()get() 方法的形式变成直接使用属性的方式改变对应的 value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python
# -*- coding: utf-8 -*-

class Student(object):

@property
def age(self):
return self._age

@age.setter
def age(self,value):
if not isinstance(value, int):
raise ValueError('age must be an integer!')
if value < 0 or value > 120:
raise ValueError('age must be 0 ~ 100!')
self._age = value

stu = Student()
stu.age = 50
print stu.age

这里特别特别要注意的是,age 值前面加了 _

如果想要该属性变成只能读,而不能写,那么不要写 setter 方法就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python
# -*- coding: utf-8 -*-

class Student(object):

_age = 24

@property
def age(self):
return self._age

# @age.setter
# def age(self,value):
# if not isinstance(value, int):
# raise ValueError('age must be an integer!')
# if value < 0 or value > 120:
# raise ValueError('age must be 0 ~ 100!')
# self._age = value

stu = Student()
print stu.age

如果加了 stu.age = 50 就会报错:

1
AttributeError: can't set attribute

__str__

用于打印属性:

1
2
3
4
5
6
7
8
9
class Student(object):
def __init__(self,name):
self.name = name

def __str__(self):
return 'Student object (name: %s)' % self.name

s = Student('zlf')
print s

结果为:

1
Student object (name: zlf)

__iter____getitem__

前者将类作用于循环:

1
2
for n in Fib():
print n

后者让类的循环可以直接使用下标:

1
2
f = Fib()
print f[5]

栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env python
# -*- coding: utf-8 -*-

class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b

def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己

def next(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100: # 退出循环的条件
raise StopIteration();
return self.a # 返回下一个值

def __getitem__(self,n):
a,b = 1,1
for x in range(n):
a,b = b,a +b
return a

for n in Fib():
print n

f = Fib()
print f[5]

结果:

1
2
3
4
5
6
7
8
9
10
11
12
8
1
1
2
3
5
8
13
21
34
55
89

__call__

让对象的实例可以直接调用:

1
2
3
4
5
6
7
8
9
class People(object):
def __init__(self,action):
self.action = action

def __call__(self):
print('People can %s .' % self.action)

p = People('talk')
p()

结果:

People can talk .

这么看来函数和对象还是很像的。

__getattr__

调用不存在的属性时使用 __getattr__() 方法可以动态返回一个属性:

1
2
3
4
5
6
7
8
9
10
11
class Student(object):

def __init__(self):
self.name = 'Michael'

def __getattr__(self, attr):
if attr=='score':
return 99

s = Student()
print s.score

结果是 99

当定义属性存在时,再定义 __getattr__ 是无效的:

1
2
3
4
5
6
7
8
9
10
11
12
class Student(object):

def __init__(self):
self.name = 'Michael'
self.score = 12

def __getattr__(self, attr):
if attr=='score':
return 99

s = Student()
print s.score

结果是 12

多继承

Python 支持多继承。那么第一个想到的问题一定是,如果两个父类中拥有相同的方法,子类怎么办?

看一段网友提供的代码就清楚了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env python
# -*- coding: utf-8 -*-

'关于多重继承的一些小测试'

__author__='bgn-037'

class A(object):
def put_out(self):
return 'A'
def print_outa(self):
return 'a'

class B(object):
def put_out(self):
return 'B'

class C(A,B):
pass

class D(B,A):
pass

class E(D):#不能同时继承A与D-->TypeError: Cannot create a consistent method resolution order (MRO) for bases A, D
def put_out(self):
return 'E'
def print_out(self):
return 'e'

X=C()
print(X.put_out())
Y=D()
print(Y.put_out())
M=E()
print(M.put_out(),M.print_out(),M.print_outa())

运行的结果是:

1
2
3
A
B
('E', 'e', 'a')

很容易理解,哪个在父类在前面,就用哪个父类中的方法。

当我一番搜索关于 Mixin 的概念之后,我觉得, Python 其实不算真正意义上的多继承,而更像是单继承多实现。

继承强调的是 I amMixin 强调的是 I can

Mixin 的使用是为了获得多继承的优点,或者说就是一种语法糖:在编译的时候将一段代码复制到了另一个地方。同时它也避免了多重继承的时候出现的类与类之间的关系过于混乱的问题。

它的使用就是在后面的父类中加个后缀:

1
2
class Dog(Mammal, RunnableMixin, CarnivorousMixin):
pass

Carnivorous 是食肉的意思,Mixin 是混入的意思

参考链接:

缪雪峰官网
Mixin 是什么概念
Python Mixin 模式
Python 多继承

未完待续…