前言:现在开始使用Pycharm作为运行代码的环境,继续延伸第五部分的知识要点。
6.1 使用__slots__
Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class能添加的属性:
class Student(object):
__slots__ = ('name','score') #不是变量名,而是字符串?
s=Student()
s.name = 'Peter'
s.gender = 'male' #无法赋值
Traceback (most recent call last):
File "C:/Users/PGH/PycharmProjects/test/test-1.py", line 6, in <module>
s.gender = 'male'
AttributeError: 'Student' object has no attribute 'gender'
__slots__
定义的属性仅对当前类起作用,对继承的子类是不起作用的。
6.2 使用@property
Python内置的@property
装饰器就是负责把一个方法变成属性调用的,也就是为了实现s.name = ''
这样的输入。
需要注意的是使用__init__
的时机是为了在创建实例的时候把必须绑定的属性填进去。
class Student(object):
@property
def score(self): #没有@的话,调用应该是按方法的模式
return self._score
@score.setter #这是`@property`自身创建的
def score(self,value):
if not isinstance(value,int):
raise ValueError('score must be an integer!')
if value < 0 or value >100:
raise ValueError('score must between 0~100!')
self._score = value
@property
def rank(self): #定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性
if self._score < 60:
return 'C'
if self._score < 90:
return 'B'
else:
return 'A'
s = Student()
s.score = 90
print s.rank
A
6.3 多重继承
多重继承指的是有好多干爹?继承所有父类的所有的功能,如下:
class Dog(Mammal,Runnable,Hastail):
pass
Mixin
其实可以把 Mixin 看作多重继承的一种在特定场景下的应用,同时也是为了更好的看出继承的关系,知道哪些是额外混入的功能。
class Dog(Mammal, RunnableMixin, CarnivorousMixin):
pass
6.4 定制类
重申一下,对于Python中__xxx__
这样的变量或者函数名,都是有特殊用途的,需要额外注意。下面再简单介绍几个。
__str__
#Windows PowerShell 环境
>>> class Student(object):
... def __init__(self,name):
... self.name = name
...
>>> Student('Peter')
<__main__.Student object at 0x02A380F0>
#不美观= =其实我觉得无所谓,只是没有反映出变量名
>>> class Student(object):
... def __init__(self,name):
... self.name = name
... def __str__(self):
... return 'Student object (name:%s)' % self.name
...
>>> print Student('Peter') #相当于调用了`__str__`方法
Student object (name:Peter)
>>> s = Student('Peter') #不用赋值也一样
>>> s # 调用的是`__repr__`方法,这是为调试服务的
<__main__.Student object at 0x02A382D0>
>>> class Student(object):
... def __init__(self,name):
... self.name = name
... def __str__(self):
... return 'Student object (name: %s)' % self.name
... __repr__ = __str__ #偷懒= =
...
>>> Student('Peter')
Student object (name: Peter)
__iter__
适用于构建一个能被for...in...
循环的类,类似于list
和tuple
,它有一个大致的流程。__iter__()
返回一个迭代对象,Python的for循环就会调用该迭代对象的next()
方法拿到下一个值,直到遇到StopIteration错误,退出循环。
class Fib(object):
def __init__(self):
self.a,self.b = 0,1
def __iter__(self):
return self
def next(self): #关键是这一个计算的过程,这是真循环体,__iter__()只是引子
self.a,self.b = self.b,self.a + self.b
if self.a > 100:
raise StopIteration();
return self.a
for i in Fib():
print i
1
1
2
3
5
...
89
__getitem__
上述例子无法实现list中取元素的功能,所以添加__getitem__()
方法。具体实现如下:
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
与之对应的是__setitem__()
方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()
方法,用于删除某个元素。要完整实现一个list的功能,那就得所有配合着来用了。
__getattr__
Python有一个机制,那就是写一个__getattr__()
方法,动态返回一个属性,比如说你调用的那个不是固定值,那么Python解释器会试图调用__getattr__(self, 'xxx')
来尝试获得属性,可以返回值,也可以返回函数。
class Student(object):
def __getattr__(self, attr):
if attr=='age':
return lambda: 25
raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr #这是一个错误约定
- 只有在没有找到属性的情况下,才调用
- 调用不存在的就返回None
- 可以针对完全动态的情况使用(不明觉厉)
__call__
一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()
来调用。通过在任何类中定义一个__call__()
方法,直接对实例进行调用。
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
s = Student('Peter')
s()
My name is Peter
通过callable()
函数,我们就可以判断一个对象是否是“可调用”对象(加括号?)。
6.5 使用元类
type()
动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。
首先学习一下对于模块的调用,首先编译好一个test.py
文件,里头一个类(Hello),然后编译器运行在该路径下,直接输入from test import Hello
。示例如下:
class Hello(object):
def hello(self,name = 'world'):
print 'hello, %s' % name
>>> from test import Hello #动态创建出一个Hello的class对象
>>> h = Hello()
>>> h.hello()
hello, world
type()
函数可以查看一个类型或变量的类型,Hello是一个class,它的类型就是type,而h是一个实例,它的类型就是class Hello
。
我们可以通过type()
函数创建出Hello类,而无需通过class Hello(object)...
的定义:
>>> def fn(self, name='world'): # 先定义函数
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
要创建一个class对象,type()
函数依次传入3个参数:
- class的名称;
- 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
- class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
其实生成的class和用class定义是一样的,这里只是为了说明一下该函数的使用,自己还是怎么方便怎么来吧。。。
metaclass(元类)
作用:控制类的创建行为。
先定义metaclass,就可以创建类,最后创建实例。只不过我们一般省略了第一步。
按照默认习惯,metaclass的类名总是以Metaclass结尾。
介于这个太复杂,我还是留到以后再慢慢研究吧= =
对于实例属性和类属性提一下,直接在class中定义的是类属性,如:
class Student(object):
name = 'Student'
实例属性必须通过实例来绑定,比如self.name = 'xxx'
,当然,属性并不一定需要最初在定义类中,然而在同时存在的时候,也就是明明相同时,优先调用的类属性,所以,千万不要把实例属性和类属性使用相同的名字。