Python基础学习(六)
前几天一直在练手廖雪峰老师的python课程,接下来继续学习,由于面向对象编程这一课相对理论便不在此练手,直接上手面向对象高级编程。
一、使用 __slots__
一般情况下一个class是可以绑定一个属性和方法的,例如:
#给实例绑定属性 和 方法 #绑定属性 class Student(object): pass s = Student() s.name = 'nihao' print(s.name)
绑定方法:
#绑定方法 def setAge(self, a): self.age = a from types import MethodType s.setAge = MethodType(setAge, s) s.setAge(22) print( s.age )
给一个实例绑定一个方法,对于另外一个实例是不起作用的
例如:
#给一个实例绑定一个方法,对于另外一个实例是不起作用的 s2 = Student() #s2.setAge(11)
说明:这里的s2.setAge(11)是会报错的。
但是给class绑定方法后,所有实例均可调用
#但是给class绑定方法后,所有实例均可调用 def setScore(self, score): self.score = score Student.setScore = setScore s.setScore(11) print( s.score ) s2.setScore(33) print( s2.score )
其他的案例将函数写在class里面,任何实例都可以调用:
class Test(object): def myFun(self, num): self.num = num t = Test() t.myFun(22) print(t.num)
总结:可以给一个实例绑定方法和属性,但是另外一个实例调用是不起作用的,除非给class绑定一个方法,或者在class里面自定义一个方法,这样才会在所有的实例中都可以调用。
使用__slots__
说明:__slots__ 的英文单词是槽的意思,也就是一个位置的意思。占用一个坑。__slots__的好处是可以限定一个class的实例绑定属性的个数和指定属性的标准。
比方说我限定了一个类只能在外部绑定哪些属性名称,通过__slots__定义的就可以使用 否则定义其他属性的 class 都不认识。
案例:
class Student(object): __slots__ = ('name', 'age') #用tuple定义允许绑定的属性名称 s = Student() s.name = 'cici' print( s.name ) s.age = '18' print( s.age ) s.score = 100 print( s.score )
输出:
cici 18 Traceback (most recent call last): File "/mnt/hgfs/webspace/pythonStudy/one.py", line 678, in <module> s.score = 100 AttributeError: 'Student' object has no attribute 'score'
由此可见,只有定义了属性名称,是可以做外部绑定的,没有定义的属性名称,是绑定不成功的。
另外需要注意的是:__slots__只对当前类起作用,对于继承的子类是不起作用的。
二、使用@property
通常我们通过实例去给一个实例绑定一个属性,或者修改属性的值,显然很不安全,可以通过如下案例实现:
class Student(object): def getScore(self): return self._score def setScore(self, val): if not isinstance(val, int): raise ValueError('请输入整型') if val <0 or val> 100: raise ValueError('范围要在0-100直间') self._score = val s = Student() s.setScore(44) print( s.getScore() )
说明:由此可见,通过设置方法,去设置参数,再通过另外一个方法去获取参数。明显比较安全些,但是这种写法稍微复杂了一点,要调用两个方法才行
python恰好有一个即可以验证参数,有可以像普通绑定属性这样简单的方式,那就是python内置的@property装饰器 装饰器就是负责把一个方法变成属性调用的形式:
案例:
class Student(object): @property def score(self): return self._score @score.setter def score(self, val): if not isinstance(val, int): raise ValueError('score is integer') elif val <0 or val >100: raise ValueError('0-100') self._score = val s = Student() s.score = 90 print(s.score)
输出:90
说明:要将一个getter方法变成属性一样的调用,只需要加上@property就可以了,另外 @property 本身又创建了一个装饰器@score.setter 负责把一个setter方法变成属性赋值,这样就可以得到一个可控的属性操作了。
三、定制类
在python中类似__xxx__的变量或则函数名说明它们是拥有特殊用途的函数。
例如:
__str__
案例:
class Student(object): def __init__(self, name): self.name = name print( Student('cici') )
输出:<__main__.Student object at 0x7fd703484940>
说明:由此可见,这种输出是非常不好看的,改一下
class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name: %s)' % self.name print( Student('cici') )
输出:Student object (name: cici)
由此可见,__str__()方法是可以返回一个好看的字符串的,而且还可以看出实例内部的数据。
__iter__
class Fib(object): def __init__(self): self.a, self.b = 0, 1 #初始化两个计数器 def __iter__(self): return self #实例本身就是迭代对象 def __next__(self): self.a, self.b = self.b, self.a+self.b #计算下一个值 if self.a > 20: #退出循环的条件 raise StopIteration() return self.a #返回下一个值 for item in Fib(): print(item)
输出:
1 1 2 3 5 8 13
说明:如果一个类想要被for ... in ... 循环,类似list 或 tuple ,就必须实现一个__iter__() 方法 返回它自己,改方法返回一个迭代对象,然后,python 的 for 循环将会不间断的调用该迭代对象的 __next__() 方法,拿到循环的下一个值,直到遇到 StopIteration 错误时退出。
__getitem__
上面定义的Fib实例虽然可以作用于for循环,看起来类似list,但是它并不能类似list一样去使用,比如取值的时候:
class Fib(object): def __getitem__(self, item): a,b = 1,1 for x in range(item): a, b = b, a+b return a print(Fib()[0]) print(Fib()[1]) print(Fib()[2]) print(Fib()[3]) print(Fib()[4]) print(Fib()[5])
输出:
1 1 2 3 5 8
说明:由此可见,若要将一个实例像list那样调用按照下标取元素,需要在定义一个__getitem__()方法。
注意: list 有一个切片方法,但是用在类里面会报错,这是由于__getitem__()传入的参数有可能是一个int 也有可能是一个切片的对象slice,所以需要做判断:
__getattr__
一般情况下,如果我们调用一个类的方法或者属性,如果不存在,就会报错,例如:
class Student(object): def __init__(self): self.name = 'cici' s = Student() print( s.name ) print( s.score )
输出:
cici Traceback (most recent call last): File "/mnt/hgfs/webspace/pythonStudy/one.py", line 771, in <module> print( s.score ) AttributeError: 'Student' object has no attribute 'score'
说明:由此可见,调用 name 的时候没有问题,而调用 score的时候说Student没有找score的元素
通过__getattr__控制
class Student(object): def __init__(self): self.name = 'cici' def __getattr__(self, item): if item == 'score': return 100 s = Student() print( s.name ) print( s.score ) print( s.aaaaa )
输出:
cici 100 None
说明:由此可见,在类中增加了一个__getattr__ 可以自定义输出的属性,另外在次访问不存在的属性或时会友好的返回None
__call__
一个对象实例可以有自己的属性和方法,当我们调用实例方法时使用 instance.method() 来调用,能不能直接在实例本身上调用呢?案例
例如:
class Student(object): def __init__(self, name): self.name = name def __call__(self): return self.name s = Student('cici') print( s() )
输出:‘cici'
说明:由此可见,如果像调用实例本身 s() 其实是可以调用成功的,因为这类中我们已经定义了一个__call__()方法,这样就实现了调用实例就好像调用函数一样的效果。
那么问题就来了,如果区分它是一个对象,还是一个函数呢?更多时候,我们需要判断一个对象是否能够被调用,能够被调用的对象就是一个Callable对象
例如:
print( callable(Student) )
返回:True
所以、通过callable()函数,就可以判断这个对象是否是 可调用的 对象。
四、使用枚举类
定义一个枚举
from enum import Enum, unique Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')) for name, member in Month.__members__.items(): print(name, '=>', member, ',', member.value)
输出:
Jan => Month.Jan , 1 Feb => Month.Feb , 2 Mar => Month.Mar , 3 Apr => Month.Apr , 4 May => Month.May , 5 Jun => Month.Jun , 6 Jul => Month.Jul , 7 Aug => Month.Aug , 8 Sep => Month.Sep , 9 Oct => Month.Oct , 10 Nov => Month.Nov , 11 Dec => Month.Dec , 12
访问枚举的方法
@unique class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6 #访问这些枚举类型的方法 day1 = Weekday.Mon print( day1 ) print( day1.value ) print( Weekday['Thu']) print( Weekday['Thu'].value) print( Weekday(2) )
输出:
Weekday.Mon 1 Weekday.Thu 4 Weekday.Tue
可见,既可以用成员名称引用枚举常量,又可以直接根据value的值获得枚举常量。
五、使用元类
。。。