您的位置:首页 > 编程语言 > Python开发

Python学习笔记(四)面向对象高级编程

2019-05-14 10:45 501 查看

1.使用__slots__:

想要限制实例能够添加的属性时,使用__slots__:

[code]class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
[code]>>> s = Student() # 创建新的实例
>>> s.name = 'Michael' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'

使用

__slots__
要注意,
__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的,除非在子类中也定义
__slots__
,这样,子类实例允许定义的属性就是自身的
__slots__
加上父类的
__slots__

2.@property:Python内置的

@property
装饰器就是负责把一个方法变成属性调用的:

[code]"""
请利用@property给一个Screen对象加上width和height属性,以及一个只读属性resolution:
"""
class Screen(object):
@property
def width(self):
return self._width

@width.setter
def width(self,value):
self._width=value

@property
def height(self):
return self._height

@height.setter
def height(self,valu):
self._height=valu

@property
def resolution(self):
return self._width * self._height
s = Screen()
s.width = 1024
s.height = 768
print('resolution =', s.resolution)
if s.resolution == 786432:
print('测试通过!')
else:
print('测试失败!')

还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性。

@property
广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

3.多重继承:通过多重继承,一个子类就可以同时获得多个父类的所有功能。

[code]class Animal(object):
pass

# 大类:
class Mammal(Animal):
pass

class Bird(Animal):
pass

# 各种动物:
class Dog(Mammal):
pass

class Bat(Mammal):
pass

class Parrot(Bird):
pass

class Ostrich(Bird):
pass
class Runnable(object):
def run(self):
print('Running...')

class Flyable(object):
def fly(self):
print('Flying...')
class Dog(Mammal, Runnable):
pass
class Bat(Mammal, Flyable):
pass

在设计类的继承关系时,通常,主线都是单一继承下来的,例如,

Ostrich
继承自
Bird
。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让
Ostrich
除了继承自
Bird
外,再同时继承
Runnable
。这种设计通常称之为MixIn。

[code]class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass

4.定制类

__str__:

[code]class friends(object):
def __init__(self,name):
self.name=name
def __str__(self):
return 'friends object (name:%s)'%self.name
print(friends("we"))

输出是:friends object (name:we),如果不用__str__,输出就会是<__main__.friends object at 0x000002590A676748>

__iter__:如果一个类想被用于

for ... in
循环,类似list或tuple那样,就必须实现一个
__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的
__next__()
方法拿到循环的下一个值,直到遇到
StopIteration
错误时退出循环。

我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:

[code]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 > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值

现在,试试把Fib实例作用于for循环:

[code]>>> for n in Fib():
...     print(n)
...
1
1
2
3
5
...
46368
75025

__getitem__:Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:Fib()[5]就不可以,要表现得像list那样按照下标取出元素,需要实现

__getitem__()
方法:

[code]class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a

现在,就可以按下标访问数列的任意一项了。

但是使用切片时对于Fib却报错。原因是

__getitem__()
传入的参数可能是一个int,也可能是一个切片对象
slice
,所以要做判断:

[code]class Fib(object):
def __getitem__(self, n):
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L

这时切片就可以使用了,但是对于step参数,负数等还是不支持,所以,要正确实现一个

__getitem__()
还是有很多工作要做的。

此外,如果把对象看成

dict
__getitem__()
的参数也可能是一个可以作key的object,例如
str

与之对应的是

__setitem__()
方法,把对象视作list或dict来对集合赋值。最后,还有一个
__delitem__()
方法,用于删除某个元素。

总之,通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。

__getattr__:正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。要避免这个错误,Python有一个机制,那就是写一个

__getattr__()
方法,动态返回一个属性。如下:

[code]class Student(object):

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

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

当调用不存在的属性时,比如

score
,Python解释器会试图调用
__getattr__(self, 'score')
来尝试获得属性,这样,我们就有机会返回
score
的值。

返回函数也是完全可以的,只是调用方式变为s.age():

[code]class Student(object):

def __getattr__(self, attr):
if attr=='age':
return lambda: 25

Python的class允许定义许多定制方法,可以让我们非常方便地生成特定的类,还有很多可定制的方法,具体参考Python的官方文档

5.枚举类

定义常量时,除了可以通过大写字母定义之外,还可以为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了

Enum
类来实现这个功能:

[code]from enum import Enum, unique

@unique
cl
4000
ass Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
print(Weekday.Mon.value)#输出为1
for name, member in Weekday.__members__.items():
print(name, '=>', member.value)#输出为Sun=>0 Mon=>1 ...Sat=>6

其中

value
属性则是自动赋给成员的
int
常量,默认从
1
开始计数。@unique装饰器可以帮我们检查保证没有重复值。

6.元类

type()
函数既可以返回一个对象的类型,又可以创建出新的类型。比如,我们可以通过
type()
函数创建出
Hello
类,而无需通过
class Hello(object)...
的定义:

[code]def fn(self, name='world'): # 先定义函数
print('Hello, %s.' % name)
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
h = Hello()
print(h.hello())
print(type(Hello))
print(type(h))

要创建一个class对象,

type()
函数依次传入3个参数:

1.class的名称;

2.继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;

3.class的方法名称与函数绑定,这里我们把函数

fn
绑定到方法名
hello
上。

metaclass:元类,详情略。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: