四川农业大学——大数据实验室第二次打卡
·python作为动态语言的灵活性表现之一为:定义了一个class,创建了一个class实例之后,我们可以给实例绑定任何属性和方法,如下:
·但是给一个实例绑定的方法,对另一个实例是不起作用的:
·那么如何达到给类的所有实例都绑定同一个方法呢?可以给class绑定方法:
·但是如果我们想要限制实例的属性怎么办?Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能够添加的属性。例子如下:
·需要注意的是,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
·在绑定属性时,如果我们直接把属性暴露出去,虽然写起来简单,但是,没办法检查参数,导致可以随意修改成绩:
·这显然不合逻辑,为了限制score的范围,可以通过一个set_score()方法来限制范围,在通过一个get_score()来获取成绩,这样,就可以在set_score()方法里面检查参数是否正确了:
class Student(object):
def get_score(self):
return self.score
def set_score(self,value):
if not isinstance(value,int):
raise ValueError('score must be integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100')
self.score = value
·但是类的方法略显复杂,没有用属性这么直接简单。对于类的方法,装饰器一样起作用,Python内置的@property装饰器就是负责把一个方法变成属性调用的:
class Student(object):
@property
def score(self):
return self._score
@score.setter
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
·还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性。比如下面的birth是可读写属性,而age就是一个只读属性:
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self,value):
self._birth = value
@property
def age(self):
return 2015 - self._birth
@property 广泛应用于类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样程序运行时就减少了出错的可能性。
·多重继承:为了避免分类过多而导致类的层次复杂性呈指数增长。如下:
·MixIn:在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是如果需要混入额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable,这种设计就称为MixIn:
class Dog(Mammal,RunnableMixIn,CarnivorousMixIn):
pass
·先定义一个Student类,打印一个实例:
·这样的打印结果不好看,只需要定义好__str__()方法,返回一个好看的字符串就可以了:
·这样不但好看,而且容易看出内容中的要点。但是直接敲变量不用print,打印出来的实例还是不好看:
·这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说__repr__()是为调试服务的。解决办法是再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的,所以,有个偷懒的写法:
class Student(object):
def __init__(self,name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
·__ iter__方法:如果一个类想被用于for…in循环,类似于list或者tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。我们以斐波那数列为例,写一个Fib类,可作用于for循环:
·__ getitem__方法:Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当list来使用还是不行,比如取第4个元素:
·要表现得像list那样按照下标取元素,需要实现__getitem__方法:
··但是上述方法不能切片,原因是因为__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:
·但是上述方法没有对step参数做处理,也没有对负数做处理:
·此外,如果把对象看成dict,__ geitem__()的参数也可能是一个可以作key的object,例如str,与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。 总之,我们把自己定义的类表现得和Python自带的list、tuple没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。
·__ getattr__方法:正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。而要避免这个错误,除了可以加上一个score属性外,Python还有另外一个机制,那就是写一个__getattr__()方法,动态返回一个属性。如下:
·返回函数也是可以的,但是注意调用方式会发生改变: