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

Python学习总结笔记(2)--类扩展小结

2016-11-16 19:46 429 查看
Python作为动态语言,灵活性之一就是支持类的动态扩展,可以动态给类对象或者实例添加属性或者方法。这一特性给Python语言带来了很大的灵活性。

0x01 属性和方法扩展

举个例子:

class Person(object):
def __init__(self,name):
self.__name=name

@property
def name(self):
return  self.__name


上面定义了一个Person类,含有一个name的属性,但是我们可能需要给他添加Age、Sex等新的属性或者函数怎么办呢?可以这么来做:

a=Person('Andy')
#添加
a.age=18
print dir(a)


运行结果:

['_Person__name', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name']

Process finished with exit code 0


动态添加了一个属性age。这是在实例中添加的,对其他实例是无效的,比如:

b=Person('kk')
print b.age


运行结果:

AttributeError: 'Person' object has no attribute 'age'


提示b中没有这样的属性。要想对类所有实例均有效,可以直接对类本身添加动态属性:

Person.age=18
b=Person('kk') print b.age


同样的,也可以动态添加方法(函数)。需要导入模块types. MethodType,如下:

from types import MethodType

def eat(self):
print 'eat'
b=Person('kk')
b.eat=MethodType(eat,b)
b.eat()


类似的,如果要求对所有类的实例有效,就需要在类本身的对象上进行动态扩展:

Person.eat=MethodType(eat,Person)
b=Person('kk')
b.eat()


需要注意一点的是,动态扩展不要涉及“私有变量”,比如:

def getName(self):
return  self.__name

Person.getName=MethodType(getName,Person)
b=Person('kk')
print b.getName()


运行结果:

AttributeError: type object 'Person' has no attribute '__name'


提示找不到属性__name,那么读取公开的属性name呢:

def getName(self):
return self.name

Person.getName=MethodType(getName,Person)
b=Person('kk')
name=b.getName()
print name


运行结果:

<property object at 0x0128D540>


虽然没有报错,但是只是打印出了获取的属性的信息,并没有获取其真实的值。



同样的给“私有变量”赋值也是无效的:

def setName(self,name):
self.__name=name

Person.setName=MethodType(setName,Person)
b=Person('kk')
b.setName('kkb7')
print b.name


打印出来的name仍然是kk。所以采用动态扩展方式时,不要对类中原有的属性和方法进行操作(当然静态变量除外)。

0x02 限制扩展

上面讲了类的扩展,如果我们需要显示随意扩展怎么办呢?比如,只能给Person扩展age和height。可以通过顶一个特殊的slots来实现。

class Person(object):
def __init__(self,name):
self.__name=name
__slots__=('__name','name','age','height','getAge')
@property
def name(self):
return  self.__name


如果试图添加其他的属性和方法,将会报错:

def getAge(self):
return 18

def getHeight(self):
return 182

b=Person('kk')
#正常
b.age=18
b.height=182
b.getAge=MethodType(getAge,b)

#异常
b.width=130
b.getHeight=MethodType(getHeight,b)


设置断点调试:



前面几个扩展一切正常,断点前进即报错:

AttributeError: 'Person' object has no attribute 'width'


0x03 类扩展

上面讲了slots可以用来限制类的属性和方法定义。类似*这样的变量在Python中非常特殊。还有一些变量可以用来扩展类的功能。下面就讲几个常见的变量

(1) len

Python中很多对象都可以用len()来计算对象的“长度”,其实是在类里面实现了函数len。比如:

class Person(object):
def __init__(self,name):
self.__name=name

__slots__=('__name','name','age','height','getAge')

@property
def name(self):
return  self.__name

#定义__len__
def __len__(self):
return len(self.__name)

b=Person('kk')
print 'b\'length is:',len(b)

c=Person('kikay')
print 'c\'length is:',len(c)


需要注意的是len函数的返回值必须是整数,否则将会报错:

class Person(object):
def __init__(self,name):
self.__name=name

__slots__=('__name','name','age','height','getAge')

@property
def name(self):
return  self.__name

#定义__len__
def __len__(self):
return 'error'

b=Person('kk')
print 'b\'length is:',len(b)


运行结果:

TypeError: an integer is required


(2)str

在类中实现str函数,就可以执行str()函数:

class Person(object):
def __init__(self,name):
self.__name=name

__slots__=('__name','name','age','height','getAge')

@property
def name(self):
return  self.__name

#定义__len__
def __len__(self):
return len(self.__name)

#定义__str__
def __str__(self):
return 'Person object (name:%s)'%self.__name

b=Person('kk')
print str(b)


运行结果:

Person object (name:kk)


(3)iter

如果类的对象希望具有迭代的效果(for…in…),那么就需要实现iter,该方法返回一个迭代对象,结合next函数,可以循环取值。用著名的Fibonacci数列的实现为例:

class Fibonacci(object):
def __init__(self):
self.__v1,self.__v2=0,1

def __iter__(self):
return self

#限制运行的值上限
__MaxVaue__=1000

def next(self):
self.__v1,self.__v2=self.__v2,self.__v2+self.__v1
if self.__v1>=self.__MaxVaue__:
#停止迭代
raise StopIteration()
return self.__v1

f=Fibonacci()
#迭代
for i in f:
print i


(4)getitem

如果先实现索引访问,需要实现getitem:

class Fibonacci(object):
def __init__(self):
self.__v1,self.__v2=0,1

def __iter__(self):
return self

#限制运行的值上限
__MaxVaue__=1000

def next(self):
self.__v1,self.__v2=self.__v2,self.__v2+self.__v1
if self.__v1>=self.__MaxVaue__:
#停止迭代
raise StopIteration()
return self.__v1

def __getitem__(self, item):
if isinstance(item,int):
temp1,temp2=1,1
for i in range(item):
temp1,temp2=temp2,temp1+temp2
return temp1
else:
raise TypeError('item must be integer')

f=Fibonacci()
print f[3]


上面是利用索引来访问,如果要支持切片功能,可以这样来实现:

class Fibonacci(object):
def __init__(self):
self.__v1,self.__v2=0,1

def __iter__(self):
return self

#限制运行的值上限
__MaxVaue__=1000

def next(self):
self.__v1,self.__v2=self.__v2,self.__v2+self.__v1
if self.__v1>=self.__MaxVaue__:
#停止迭代
raise StopIteration()
return self.__v1

def __getitem__(self, item):
if isinstance(item,int):
temp1,temp2=1,1
for i in range(item):
temp1,temp2=temp2,temp1+temp2
return temp1
elif isinstance(item,slice):
L=[]
temp1,temp2=1,1
start=item.start
stop=item.stop
if start is None:
start=0
for i in range(start,stop):
if i>=start:
L.append(temp1)
temp1,temp2=temp2,temp1+temp2
return L

else:
raise TypeError('item must be integer')

f=Fibonacci()
print f[3]
print f[2:8]


当然上面的切片实现是单步的,如果step不为1,就得修正代码实现逻辑了。这里就不再赘述这一过程,只做下getitem的使用总结。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python 扩展