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

Python学习笔记13:Python面向对象编程

2016-07-30 23:01 856 查看
1、引言

(1)类和实例:类是对象的定义,实例是“真正的实物”。

定义类:类名通常大写字母打头。

class MyNewObjectType(bases):
'define MyNewObjectType class'
class_suite


bases可以是一个(单继承)或多个(多重继承)用于继承的父类。

object是“所有类之母”。

Python调用类进行实例化,实例化类不使用new关键字。

>>>c=MyNewObjectType()


类有时可以仅作为名称空间。

class MyData(object):
pass
mathobj=MyData()
mathobj.x=4
mathobj.y=5


这些属性是动态的,不需要在构造器中或其他任何地方为它们预先声明或赋值。

(2)方法

定义方法:属性和方法使用驼峰记法。

class MyDataWithMethod(object):
def printFoo(self):
print ....


所有方法都存在self,表示实例对象本身。

静态方法或类方法不需要self。

__init__()方法类似于构造函数,但不同于构造函数,因Python不new。该方法在实例被创建后,实例化调用返回这个实例之前被调用。

2、面向对象编程常用术语

抽象/实现:建模 现实化

封装/接口

合成:联合、聚合

继承/派生

泛化/特化

多态

自省/反射

3、类

Python中,一切皆为对象。

下面是类的定义语法。

class ClassName(object):
'class documentation string'
class_suite


Python不支持纯虚函数(像C++)或者抽象方法等。

替代方案:在基类方法中引发NotImplementedError异常。

4、类属性

(1)属性

属性(数据或函数),使用句点属性标识符来访问。

属性本身也是一个对象,也有自己的属性,所以访问属性时会形成一个属性链。

(2)类属性/实例数据属性

实例属性在OOP中用得最多,类属性仅当需要有更加“静态”数据类型时才变得有用,它和任何实例无关,方法是类属性。

Python要求,没有实例,方法不能被调用。方法必须“绑定”到一个实例才能直接被调用。非绑定的方法可能可以被调用,但实例对象一定要明确给出,才能保证调用成功。然而,不管是否绑定,方法都是它所在的类的固有属性,即使它们几乎总是通过实例来调用的。

(3)确定一个类有哪些属性的方法

使用内建函数dir()

访问类的字典属性__dict__

内建函数vars()接受类对象作为参数,返回类的__dict__属性的内容。

(4)特殊的类属性

C.__name__ 类C的名字(字符串)

C.__doc__ 类C的文档字符串

C.__bases__ 类C的所有父类构成的元组

C.__dict__ 类C的属性

C.__module__ 类C所在的模块(1.5)

C.__class__ 实例C对应的类(新式类)

5、实例

Python 2.2中统一了类和类型。

Python通过调用类对象来创建实例。

①__init__()方法

当类被调用,创建实例对象,对象创建后,调用__init__()方法完成特别的操作,执行完返回类对象,实例化结束。

Python没有使用new创建实例,没有定义构造器,由Python创建对象。

②__new__()方法

“构造器”方法,与__init__()方法相比,__new__()更像一个真正的构造器,因为__new__()必须返回一个合法的实例,该实例作为self传给__init__()方法。

__new__()方法会调用父类的__new__()方法来创建对象。

在对内建类型进行派生时,__new__()方法可以实例化不可变对象。

③__del__()方法

“解构器”方法,当实例对象所有的引用都被清除掉后才执行该方法,用于实例释放前进行特殊处理。

__del__()方法只能被调用一次。

使用__del__()方法,不要忘记首先调用父类的__del__()方法。

del x不表示调用x.__del__()方法,仅引用计数减少。

若存在循环引用,则对象的__del__()方法可能永远不会被执行。

__del__()方法未捕获的异常会被忽略掉,除非有必要,否则不去实现__del__()方法。

如果定义了__del__()方法,且实例是某个循环的一部分,垃圾回收器将不会终止这个循环,你需要自己显式调用del。

6、实例属性

方法严格来说是类属性。实例仅拥有数据属性。

(1)”实例化”实例属性

①在__init__()方法中设置实例属性。

设置实例的属性可以在实例创建后任意时间进行。__init__()方法是设置这些属性的关键点之一。Python能够在“运行时”创建实例属性(Python优秀特性之一)

②默认参数提供默认的实例安装。

class HotelRoomClac(object):
def __init__(self,rt,sales=0.085,rm=0.1):
self.salesTax=sales
self.roomTax=rm
self.roomRate=rt


③__init__()方法应该返回None

__init__()方法不应该返回任何对象,因为实例对象是自动在实例化调用后返回的。

(2)查看实例属性

查看实例属性:dir()、__dict__属性、vars()

特殊的实例属性:I.__class__、I.__dict__

内建类型属性:内建类型可以使用dir()方法,不可以访问__dict__特殊属性,因为在内建类型中,不存在这个属性。

(3)类属性和实例属性(类似于自动变量和静态变量)

可以采用类来访问类属性,若实例没有同名的属性的话,也可以用实例来访问。

类属性可以通过类或实例来访问,不过只能使用类访问类属性时,才能更新类属性的值。若在实例中更新类属性,将会创建同名的实例属性,“遮蔽”了类属性。当删除同名的实例属性,类属性才起作用。所以,从实例中访问类属性须谨慎。

class C(object): #定义类
version = 1.2#静态成员
>>>c=C()
>>>C.version #通过类来访问
>>>c.version #通过实例来访问
>>>C.version+=0.1 #通过类(只能这样)来更新类属性
>>>c.version =1.3 #任何对实例属性的赋值都会创建一个实例属性,而不是更新类属性


当类属性是可变类型时,并不会创建实例属性,直接操作的是类属性。

class Foo(object):
x={2003:'poe2'}
>>>foo=Foo()
>>>foo.x[2004]='valid path'
>>>foo.x
{2003:'poe2',2004:'valid path'}
>>>Foo.x
{2003:'poe2',2004:'valid path'} #生效了
>>>del foo.x #删除会报错,因为没有遮蔽所以不能删除掉


(4)类属性持久性

类属性,任凭整个实例(及其属性)的如何进展,他都不理不睬(因此独立于实例),类属性的修改会影响到所有的实例。类属性是静态成员。

7、绑定和方法调用

(1)绑定

方法仅仅是类内部定义的函数,意味着方法是类属性而不是实例属性。

方法只有在类拥有实例时,才能被调用。方法被认为是绑定到实例。方法中的变量self表示调用此方法的实例对象。

(2)方法调用

①调用非绑定的方法(不常见):类还未实例化。

class EmplAddrBookEntry(AddrBookEntry):
'Employee Address Book Entry class'
def __init__(self,nm,ph,em):
AddrBookEntry.__init__(self,nm,ph) #覆盖父类方法
self.empid=em


②调用绑定方法:类已经实例化。

>>>mc=MyClass()
>>>mc.foo()


总结:方法定义于类内部,是类方法;方法绑定到实例,由实例调用;未绑定,由类调用。

8、静态方法和类方法(2.2)

(1)经典类中创建静态方法和类方法的例子

class TestStaticMethod:
def foo():
print 'calling static method foo()'
foo=staticmethod(foo)  #内建函数,将方法转换成静态方法
class TestClassMethod:
def foo(cls):  #cls为类对象,类似于self
print 'calling class method foo()'
foo=classmethod(foo)   #内建函数,将方法转换成类方法


可以通过类或者实例调用这些函数。

>>>tsm=TestStaticMethod()
>>>TestStaticMethod.foo()
>>>tsm.foo()
>>>tcm=TestClassMethod()
>>>TestClassMethod.foo()
>>>tcm.foo()


(2)使用函数修饰符创建静态方法和类方法的例子(2.4)

class TestStaticMethod:
@staticmethod
def foo():
print 'calling static method foo()'
class TestClassMethod:
@classmethod
def foo(cls):
print 'calling class method foo()'


9、继承

(1)通过继承覆盖方法

子类定义与基类相同的方法时,会覆盖(override)基类方法。

子类可以使用调用非绑定的基类方法的方法调用基类方法。

也可以使用super()内建方法调用基类方法。

当从一个带构造器__init__()的类派生,如果你不去覆盖__init__(),它将会被继承并自动调用,但如果你在子类中覆盖了__init__(),子类被实例化时,基类的__init__()就不会被自动调用。若要调用父类的__init__()方法,需要使用super()。

(2)从标准类型派生

经典类中,不能对标准类型进行子类化。

2.2后,可以对标准类型进行子类化。

子类化Python类型:其中一个是可变类型;另一个是不可变类型。

①子类化不可变类型

class RoundFloat(float):
def __new__(cls,val):
return float.__new__(cls,round(val,2))


所有的__new__()方法都是类方法,所以显式地传入类作为第一个参数。

class RoundFloat(float):
def __new__(cls,val):
return super(RoundFloat,cls).__new__(cls,round(val,2))


通常使用super()内建函数去捕获对应的父类以调用它的__new__()方法。

②子类化可变类型

class SortedKeyDict(dict):
def keys(self):
return sorted(super(SortedKeyDict,self).keys())


(3)多重继承中方法解释顺序(MRO)

2.2之前,算法简单:深度优先,从左至右进行搜索,取得在子类中使用的属性。多重继承取找到的第一个名字。

2.2提出新的MRO,算法思想是根据每个祖先类的继承结构编译出一张列表,包括搜索到的类,按策略删除重复的。

2.3使用新的C3算法替换,采用广度优先。

新式类有__mro__属性,告诉你查找顺序。

新式类使用经典类的MRO会失败。

菱形效应



使用经典类的MRO,当实例化D时,不再得到C.__init__()之结果,而得到object.__init__()之结果。使用新式类,需要出现基类,这样在继承结构中,就形成了一个菱形。

补充:文档字符串不会从基类中继承过来。因为文档字符串对类,函数/方法,还有模块来说都是唯一的。

10、类、实例、其他对象的内建函数

(1)issubclass()

布尔函数,判断一个类是另一个类的子类或子孙类(一个类可视为其自身的子类)。

issubclass(sub,sup)


从2.3开始,第二个参数可以是可能的父类组成的元组。只要sub是其中任何一个的子类都返回True。

(2)isinstance()

布尔函数,判定一个对象是否是另一个给定类的实例。

isinstance(obj1,obj2)


obj1是obj2的一个实例,或是obj2的子类的一个实例时,返回True。

从2.2开始,obj2可以是一个元组,obj1是obj2元组中任何一个候选类型或类的实例时,就返回True。

(3)hasattr(),getattr(),setattr(),delattr()

*attr()系列函数可工作于各种对象,不限于类和实例。

*attr(obj,’attr’….)相当于操作obj.attr。

hasattr()布尔函数,决定一个对象是否有一个特定的属性。

getattr(),setattr()相应地取得和赋值给对象的属性。getattr()会在你试图读取一个不存在的属性时,引发AttributeError异常。

delattr()删除属性。

(4)dir()

可用于实例或者类或者模块。

用于实例,显示实例变量,还有在实例所在的类及所有它的基类中定义的方法和类属性。

用于类,显示类及它所有基类的__dict__中的内容,但不会显示定义在元类中的类属性。

用于模块,显示模块的__dict__的内容。

dir()不带参数时,显示调用者的局部变量。

(5)super()(2.2)

帮助程序员找出相应的父类,然后调用相关属性。

super(type[,obj])返回type的父类,传入obj参数进行父类绑定。

obj是一个实例,isinstance(obj,type)必须返回True;

obj是一个类或类型,issubclass(obj,type)必须返回True。

super(MyClass,self).__init__()


(6)vars()

与dir()相似,只是给定的对象参数都必须有一个__dict__属性。如果提供的对象没有一个这样的属性,则会引发一个TypeError异常。

vars()返回一个字典,包含存储于对象__dict__中的属性(键)和值。如果没有为vars()提供参数,将显示一个包含本地名称空间的属性(键)及其值的字典,也就是locals()。

11、用特殊方法定制类

Python特殊方法可以用来扩充类的功能,可以实现:

模拟标准类型;

重载操作符。

(1)Python中用来定制类的特殊方法

①基本定制型

C.__init__(self[,arg1,…]) 构造器(带一些可选的参数)

C.__new__(self[,arg1,…])构造器(带一些可选的参数);通常用在设置不变数据类型的子类

C.__del__(self) 解析器

C.__str__(self) 可打印的字符输出;内建str()及print语句

C.__repr__(self) 运行时的字符串输出;内建repr()和“操作符

C.__unicode__(self) Unicode字符串输出:内建unicode()

C.__call__(self,*args) 表示可调用的实例

C.__nonzero__(self) 为object定义False值;内建bool()

C.__len__(self) “长度”(可用于类);内建len()

②对象(值)比较

C.__cmp__(self,obj) 对象比较:内建cmp()

C.__lt__(self,obj) and C.__le__(self,obj) 小于/小于或等于:对应<及<=操作符

C.__gt__(self,obj) and C.__ge__(self,obj) 大于/大于或等于:对应>及>=操作符

C.__eq__(self,obj) and C.__ne__(self,obj) 等于/不等于:对应==,!=及<>操作符

③属性

C.__getattr__(self,attr) 获取属性:内建getattr(),仅当属性没有找到时调用

C.__setattr__(self,attr,val) 设置属性

C.__delattr__(self,attr) 删除属性

C.__getattribute__(self,attr) 获取属性:内建getattr(),总是被调用

C.__get__(self,attr) (描述符)获取属性

C.__set__(self,attr,val) (描述符)设置属性

C.__delete__(self,attr) (描述符)删除属性

④数值类型:二元操作符

C.__*add__(self,obj) 加:+操作符

C.__*sub__(self,obj) 减:-操作符

C.__*mul__(self,obj) 乘:*操作符

C.__*div__(self,obj) 除:/操作符

C.__*truediv__(self,obj) True除:/操作符

C.__*floordiv__(self,obj) Flooor除://操作符

C.__*mod__(self,obj) 取模/取余:%操作符

C.__*divmod__(self,obj) 除和取模:内建divmod()

C.__*pow__(self,obj[,mod]) 乘幂:内建pow(),**操作符

⑤数值类型:二进制操作符

C.__*lshift__(self,obj) 左移位:<<操作符

C.__*rshift__(self,obj) 右移位:>>操作符

C.__*and__(self,obj) 按位与:&操作符

C.__*or__(self,obj) 按位或:|操作符

C.__*xor__(self,obj) 按位与或:^操作符

⑥数值类型:一元操作符

C.__neg__(self) 一元负

C.__pos__(self) 一元正

C.__abs__(self) 绝对值,内建abs()

C.__invert__(self) 按位求反,~操作符

⑦数值类型:数值转换

C.__complex__(self,com) 转为complex(复数),内建complex()

C.__int__(self) 转为int,内建int()

C.__long__(self) 转为long,内建long()

C.__float__(self) 转为float,内建float()

⑧数值类型:基本表示法(String)

C.__oct__(self) 八进制表示,内建oct()

C.__hex__(self) 十六进制表示,内建hex()

⑨数值类型:数值压缩

C.__coerce__(self,num) 压缩成同样的数值类型,内建coerce()

C.__index__(self) 在有必要时,压缩可选的数值类型为整型(比如用于切片索引等)

⑩序列类型

C.__len__(self) 序列中项的数目

C.__getitem__(self,ind) 得到单个序列

C.__setitem__(self,ind,val) 设置单个序列元素

C.__delitem__(self,ind) 删除单个序列元素

C.__getslice__(self,ind1,ind2) 得到序列片段

C.__setslice__(self,ind1,ind2,val) 设置序列片段

C.__delslice__(self,ind1,ind2) 删除序列片段

C.__contains__(self,val) 测试序列成员:内建in关键字

C.__*add__(self,obj) 串联:+操作符

C.__*mul__(self,obj) 重复:*操作符

C.__iter__(self) 创建迭代器:内建iter()

⑪映射类型

C.__len__(self) mapping中项的数目

C.__hash__(self) 散列(hash)函数值

C.__getitem__(self,key) 得到给定键(key)的值

C.__setitem__(self,key,val) 设置给定键(key)的值

C.__delitem__(self,key) 删除给定键(key)的值

C.__missing__(self,key) 给定键如果不存在字典中,则提供一个默认值

(2)简单定制

自己实现init(),str(),repr()等。

print使用str()方法,真正的字符串对象表示使用repr()方法。

#! /usr/bin/env python
class RoundFloatManual(object):
def __init__(self,val):
assert isinstance(val,float),\
"Value must be a float!"
self.value=round(val,2)
def __str__(self):
return '%.2f' % self.value
__repr__=__str__


(3)数值定制

重载__add__()方法,就重载了(+)操作符。

还可以使用__radd__()方法和__iadd__()方法。

def __add__(self,other):
return self.__class__(self.hr+other.hr,self.min+other.min)


覆盖“原位”操作,实现增量赋值(2.0),比如iadd()支持mon+=tue。

(4)定制迭代器

实现类中的__iter__()和next()方法来创建一个迭代器。

#! /usr/bin/env python
class AnyIter(object):
def __init__(self,data,safe=False):
self.safe=safe
self.iter=iter(data)
def __iter__(self):
return self
def next(self,howmany=1):
retval=[]
for eachItem in range(howmany):
try:
retval.append(self.iter.next())
catch StopIteration:
if self.safe:
break
else:
raise
return retval


12、私有化

类中属性默认情况下是“公开的”,类所在模块以及导入类所在模块中的代码都可以访问到。

(1)双下划线

Python使用双下划线(__)来“混淆”属性,不允许直接访问。

混淆后的属性,会在名字前面加上下划线和类名,比如NumStr类中的__num属性,被混淆后,用于访问这个数据值的标识符就变成了self._NumStr__num。混淆操作可以防止在父类或子类中的同名冲突。

(2)单下划线

使用单下划线(_)实现简单的模块级私有化。

13、授权

(1)包装

包装任何类型作为一个类的核心成员,使新对象的行为模仿你想要的数据类型中已经存在的行为,且去掉不希望存在的行为。扩充Python是包装的另一种形式。

(2)实现授权

授权是包装的一个特性。

授权的过程即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。实现授权的关键点是覆盖__getattr__()方法,在代码中包含一个对getattr()内建函数的调用。

class WrapMe(object):
def __init__(self,obj):
self.__data=obj
def get(self):
return self.__data
def __repr__(self):
return 'self.__data'
def __str__(self):
return str(self.__data)
def __getattr__(self,attr):
return getattr(self.__data,attr)
>>>wrappedComplex=WrapMe(3.5+4j)
>>>wrappedComplex.real


访问real属性时,Python解释器将试着在局部名称空间中查找那个名字;若没有找到,会搜索类名称空间,以一个类属性访问;若还没有找到,搜素则对原对象开始授权请求,此时调用__getattr__()方法,__getattr__()方法中调用getattr()方法得到一个对象的默认行为。

总结:通过覆盖__getattr__()方法实现授权。

授权只能访问属性,特殊行为不可以。例如对列表的切片操作,它内建于类型中,不是属性,不能授权访问。

属性可以是数据属性,还可以是函数或者方法。Python所有数值类型,只有复数拥有属性:数据属性和conjugate()内建方法。

>>>wrappedList=WrapMe([123,'foo',45.67])
>>>wrrapedList[3]   #会抛出AttributeError


此时可以采用“作弊”的方法来访问实际对象和它的切片能力。

>>>realList=wrappedList.get()  #get()方法取得对原对象的访问
>>>realList[3]


14、新式类的高级特性(2.2+)

(1)新式类的通用特性

类型和类的统一,使得可以子类化Python数据类型。同时,所有的Python内建的“casting”或转换函数现在都是工厂函数。例如:int(),long(),float(),complex;str(),unicode();list(),tuple();type()。

另外,还加入了一些新的函数:basestring();dict();bool();set(),frozenset();object();classmethod();staticmethod();super();property();file()。这些类名和工厂函数,不仅能创建这些类名的新对象,还可以用来作为基类,去子类化类型。现在还可以用于isinstance()内建函数,isinstance()函数在obj是一个给定类型的实例或其子类的实例时返回True。

OLD(not as good):
if type(obj)==type(0)...
if type(obj)==types.IntType...
BETTER:
if type(obj) is type(0)...
EVEN BETTER:
if isinstance(obj,int)...
if isinstance(obj,(int,long))...
if type(obj) is int...


(2)__slots__类属性

__dict__属性跟踪所有实例属性。

实例inst,属性foo,那么inst.foo与inst.__dict__[‘foo’]等价。

字典会占用大量内存,为内存上的考虑,可用__slots__属性替代__dict__。

__slots__是一个类变量,由一序列型对象组成。由所有合法标识构成的实例属性的集合来表示。任何试图创建一个其名不在__slots__中的名字的实例属性都将导致AttributeError异常。带__slots__属性的类定义不会存在__dict__属性了。使用__slots__属性的目的是节约内存。使用__slots__属性可以防止用户随心所欲的动态增加实例属性。

class SlottedClass(object):
__slots__=('foo','bar')
>>>c=SlottedClass()
>>>c.foo=42
>>>c.xxx='nihao'  #引发AttributeError异常


(3)__getattribute__()特殊方法

Python类有一个__getattr__()的特殊方法,仅当属性不能在实例或类或祖先类的__dict__属性中找到时,才被调用。__getattribute__()与__getattr__()类似,不同在于,当属性被访问时,它就一直可以被调用,而不局限于不能找到的情况。在同时定义了__getattribute__()及__getattr__()方法的类中,除非明确从__getattribute__()方法调用,或者__getattribute__()方法引发了AttributeError异常,否则后者不会被调用。如果将要在__getattribute__()方法中访问这个类或其祖先类的属性,应该总是调用祖先类的同名方法,避免引起无穷递归。

(4)描述符(描述符就是可重用的属性)

可认为描述符是表示对象属性的一个代理,它为属性提供了强大的API。当需要属性时,可以通过描述符来访问它(当然还可以使用常规的句点属性标志法来访问属性)。

__get__(),__set__(),__delete__()特殊方法分别用于得到一个属性的值,对一个属性进行赋值,删除掉某个属性。同时覆盖__get__()和__set__()的类被称作数据描述符。实现了__set__()方法的类被称作非数据描述符,或方法描述符。

__get__(),__set__(),__delete__()的原型如下:

__get__(self,obj,typ=None)=>None

__set__(self,obj,val)=>None

__delete__(self,obj)=>None

整个描述符系统的心脏是__getattribute__()特殊方法,因为对每个属性的访问都会调用这个特殊的方法。

举例来说,给定类X和实例x:

访问实例属性,x.foo由__getattribute__()转化成:

type(x).__dict__['foo'].__get__(x,type(x))


访问类属性,那么None将作为对象被传入:

X.__dict__['foo'].__get__(None,X)


访问父类属性,super(Y,obj).foo(假设Y为X的子类):

X.__dict__['foo'].__get__(obj,X)


静态方法、类方法、属性,甚至所有的函数都是描述符。Python中函数之间的唯一区别在于调用方式的不同,分为绑定和非绑定狼类,函数描述符可以处理这些问题,描述符会根据函数的类型确定如何“封装”这个函数和函数被绑定的对象,然后返回调用对象。使用描述符的顺序很重要,有一些描述符的级别要高于其他的。描述符是一个类属性,因此所有的类属性皆具有最高的优先级。优先级排序:类属性>数据描述符>实例属性>非数据描述符>默认为__getattr__()。

#! /usr/bin/env python

import os
import pickle

class FileDescr(object):
saved=[]

def __init__(self,name=None):
self.name=name

def __get__(self,obj,typ=None):
if self.name not in FileDescr.saved:
raise AtrributeError,"%r used before assignment" % self.name
try:
f=open(self.name,'r')
val=pickle.load(f)
f.close()
return val
except(pickle.UnpicklingError,IOError,EOFError,AttributeError,ImportError,IndexError),e:
raise AttributeError, "could not read %r" % self.name

def __set__(self,obj,val):
f=open(self.name,'w')
try:
pickle.dump(val,f)
FileDescr.saved.append(self.name)
except(TypeError,pickle.PickingError),e:
raise AttributeError, "could not pickle %r" % self.name
finally:
f.close()

def __delete__(self,obj):
try:
os.unlink(self.name)
FileDescr.saved.remove(self.name)
except(OSError,ValueError),e:
pass
>>>class MyFileValClass(object):
...         foo=FileDescr('foo')
...         bar=FileDescr('bar')
>>>fvc=MyFileVarClass()
>>>print fvc.foo  #引发AttributeError
>>>fvc.bar=42
>>>print fvc.bar  #打印42


(5)property()内建函数

属性是一种有用的特殊类型的描述符。属性用来处理所有实例属性的访问。

使用句点符号访问实例属性,其实是在修改实例的__dict__属性。

使用property()访问实例属性,使用的是函数(或方法)。

property(fget=None,fset=None,fdel=None,doc=None)


property()接受一些传进来的函数作为参数,property()是在它所在的类被创建时被调用的,传进来的函数都是非绑定的。

class HideX(object):
def __init__(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
def get_x(self):
return ~self.__x
def set_x(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
x=property(get_x,set_x)


改进代码==>

class AdvancedHideX(object):
def __init__(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
@property
def x():
def fget(self):
return ~self.__x
def fset(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
return locals()


改进后的代码:类名称空间更加简洁;用户不能通过inst.set_x(40)来给属性赋值,只能使用init.x=4。

(6)元类和__metaclass__

元类是一个类(一个类中类),它的实例是其他的类。当创建一个新类时,就是在使用默认的元类,它是一个类型对象(传统类的元类是types.ClassType).当某个类调用type()函数时,就会看到它到底是谁的实例。元类一般用于创建类,在执行类定义时,解释器必须知道类正确的元类,所以解释器会寻找类属性的__metaclass__属性,若没找到,会向上查找父类的__metaclass__属性,如果还没找到,解释器会检查名字为__metaclass__的全局变量,若还不存在,就用types.ClassType作为此类的元类。在类定义时,将检查此类正确的元类,元类通常传递三个参数到构造器:类名,从基类继承数据的元组和(类的)属性字典。通过定义一个元类可以“迫使”程序员按照某种方式实现目标类,这样既可以简化他们的工作,也可以使编写出的程序更符合特定标准。

# coding=gbk

#! /usr/bin/env python

from warnings import warn

class ReqStrSugRepr(type):

def __init__(cls,name,bases,attrd):
super(ReqStrSugRepr,cls).__init__(name,bases,attrd)

if '__str__' not in attrd:
raise TypeError('Class requires overriding of __str__()')

if '__repr__' not in attrd:
warn('Class suggests overriding of __repr__()\n',stacklevel=3)

print '*** Defined ReqStrSugRepr (meta)class \n'

class Foo(object):
__metaclass__=ReqStrSugRepr

def __str__(self):
return 'Instance of class:',self.__class__.__name__

def __repr__(self):
return self.__class__.__name__

print '*** Defined Foo Class \n'

class Bar(object):
__metaclass__=ReqStrSugRepr

def __str__(self):
return 'Instance of class:',self.__class__.__name__

print '*** Defined Bar Class\n'

class FooBar(object):
__metaclass__=ReqStrSugRepr

print '*** Defined FooBar Class\n'


14.其他模块

UserList 提供一个列表对象的封装类

UserDict 提供一个字典对象的封装类

UserString 提供一个字符串对象的封装类

types 定义所有Python对象的类型再标准Python解释器中的名字

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