1. 类
1.1 python使用关键字 class 来创建类。class后跟类名,类名子后的括号中是继承的父类。若无父类,则从 object 基类中继承。
class ClassName(bases): # 继承自"bases"
'class documentation string' # class的文档字符串
pass
class ClassName2(object): # 无父类,则从object基类继承
pass
2 类属性
2.1 静态变量/静态数据
静态变量是与类绑定的,不依赖于实例,通常仅用来跟踪与类相关的值。
>>> class C(object):
... C.foo = 100
...
>>> print C.foo
100
>>> C.foo += 1
>>> print C.foo
101
2.2 Methods
方法是作为类定义中的一部分的函数,仅能通过实例来调用。像调用函数那样直接调用方法名字、或者由类对象调用都会异常
2.3 类的属性
要知道一个类有哪些属性,两种方法:①使用 dir() 内建函数 ②通过访问类的字典属性"__dict__",这是所有类都具备的特殊属性。
首先定义一个类:
# -*- coding: utf-8 -*-
class MyClass(object):
'MyClass class definition'
myVersion = '1.1'
def showMyVersion(self):
print MyClass.myVersion
对该类调用内建函数 dir() 和字典属性"__dict__"结果如下:
从上面可以看出,dir()返回对象属性的列表,__dict__返回一个对象属性为key、相应的属性对象的数据值为value的字典。内建的vars()函数则接受类对象,返回内容和__dict__相同。
2.4 类的特殊属性
对一个类C,下图显示了它所具有的所有特殊属性
对之前定义的MyClass,其相应的返回对象如下:
__name__ 适合只需要类名字的字符串表示,而不是类对象本身的情况,一些内建类型也有这个属性:
__doc__ 是类的文档字符串,类似函数/模块,是紧跟第一行下面的字符串,且不能被子类继承。
__bases__ 包含一个由所有父类组成的元组
__module__ 用来定位类的位置,当类没有使用全名时尤为重要:
>>> class C(object):
... pass
...
>>> C
<class '__main__.C'>
>>> C.__module__
'main'
>>> from mymod import C
>>> C
<class 'mymod.C'>
>>> C.__module__
'mymod'
3. 实例
类被实例化得到实例,实例的类型就是被实例化的类。
3.1 __init__() 和 __new__()
两者之间区别与联系参见 http://www.cnblogs.com/ifantastic/p/3175735.html
3.2 __del__() 解构器方法
Python的 __del__() 是在实例的引用计数为0即被释放前提供特殊处理的方法,通常不被实现,因为很少会显式的释放实例。
下面是一个运用了 __init__() 和 __del__() 的例子
>>> class C(P):
... def __init__(self):
... print "initialized"
... def __del__(self):
... P.__del__(self)
... print 'deleted'
...
>>> c1 = C()
initialized
>>> c2 = c1
>>> c3 = c1
>>> id(c1), id(c2), id(c3)
(11938912, 11938912, 11938912)
>>> del c1
>>> del c2
>>> del c3
deleted
关于 __del__() 的一些注意事项:
3.3 运用 __init__() 和 __del__() 记录实例创建次数的案例
4. 实例的属性
实例仅拥有数据属性,它是与某个类的实例相关的值,通过句点来访问,且独立于其他实例或类。
4.1 实例化实例属性
设置实例属性可以在实例创建后,也可以在初始化时 __init__中创建。下面是使用默认参数进行实例化的一个例子
class HotelRoomCalc(object):
'Hotel room rate calculator'
def __init__(self, rt, sales=0.085, rm=0.1):
'''HotelRoomCalc default arguments:
sales tax == 8.5% and room tax == 10%'''
self.salesTax = sales
self.roomTax = rm
self.roomRate = rt
def calcTotal(self, days=1):
'Calculate total; default to daily rate'
daily = round((self.roomRate *
(1 + self.salesTax + self.roomTax)), 2)
return float(days) * daily
其用法如下:
4.2 查看实例属性
内建函数dir()也可以查看实例属性;实例也有一个__dict__特殊属性,返回实例的属性构成的一个字典,可以用 vars() 传入实例来获取。
4.3 实例属性和类属性
4.3.1.访问类属性
类属性可以通过类或实例来访问,前提是没有在实例中重定义相同名字的属性。另外,对于不可变的属性对象,只有类引用类的属性时,才能更新其值;若尝试在实例中更新,则会创建一个同名的实例属性;对于可变对象,在实例中更新属性会影响类中定义的属性,所以需谨慎操作。例子如下:
4.3.2. 类属性持久性
类属性的修改会影响所有实例,当实例在类修改后才创建,那么更新的值将生效
5.静态方法和类方法
静态方法仅是类中的函数,不需要实例即可调用,通过内建函数 "staticmethod()"来实现。类方法需要类而不是实例作为第一个参数,由解释器传入,类不需要特殊命名,但通常用"cls"作为变量名,通过内建函数 "classmethod()"实现。下面是一个例子:
class TestStaticMethod:
@staticmethod
def foo():
print "Calling static method foo()"
class TestClassMethod:
@classmethod
def foo(cls):
print "Calling class method foo()"
print "foo() is part of class:", cls.__name__
输出:
6. 继承
子类可以继承其基类/父类的所有属性和方法。文档字符串除外,它不会被继承
6.1 __bases__
下面的例子可以说明 __bases__属性与继承的关联:
该例中,C是B的子类,同时通过B间接是A的子类,但声明中C的父类是B,所以其__bases__属性显示B;而D则是声明中就多重继承自A、B,所以其__bases__属性中显示A、B
6.2 多重继承
经典类的方法解释顺序是从左到右,深度优先;新式类则是广度优先,而不是深度优先。
7.类、实例和其他对象的内建函数
7.1 issubclass()
issubclass(),布尔函数,判断一个类是否是另一个类的子类或子孙类,语法是 issubclass(sub, sup)。当给定的类sub是sup(sup可以是一个可能的父类组成的元组)的子类时,返回True,否则返回False。其判断是不严格的,一个类是可以视为自身的子类的。
7.2 isinstance()判断一个对象是否是另一个给定类的实例。给定类也可以是一个可能的类型组成的元组
7.3 *attr()
*attr() 系列函数可以作用于多种对象,不局限于类。hasattr()是布尔函数,用来检查一个对象是否有一个特定的属性。getattr()取得相应对象的属性,当属性不存在且未给出可选的默认参数时,会抛出AttributError;setattr()则要么加入一个新属性,要么取代一个已存在的属性;delattr()会从一个对象中删除属性
下面是使用这一系列内建函数的例子
7.4 类和类实例的内建函数包括以下各种:
8. 特殊方法定制类
python提供了一些特殊方法用于扩充类,允许类通过重载标准操作符+、*甚至是下标或映射操作[]实现模拟标注类型。下表是所有特殊方法及描述:
8.1 基本定制
用RoundFloat例子来演示__str__和__repr__的用法。先看没有定义特殊方法时的输出:
用了断言以后,rfm输入的数据不是浮点型时会被抛出异常;rfm实例输入时浮点型时,得到的却不是想要的,即使用print 也不行。添加__str__方法后输出如下:
在定义了__str__()以后,print语句可以打印出想要的信息,但是命令后解释器中直接输入对象时显示的信息仍有问题,这可以通过覆盖__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__
输出如下:
8.2 数值定制
通过实现加法和增量加法,来理解稍微复杂的特殊定制类。
注意点:①__str__()和__repr__()定制更有意义的输出
②+重载__add__,+=重载__iadd__,obj+self用__radd__重载
③计算出个别的总数以后,调用类构造器返回一个新的对象。此时不直接通过调用类名来构造类,而是用"self.__class__"属性调用,可以避免一些问题。
④当试图重载一个没有定义的操作符时,会抛出TypeError。
⑤在增量赋值时,区别在于__i*__方法必须返回self。
源代码:
#!/usr/bin/env python
class Time60(object):
'Time60 - track hours and minutes'
def __init__(self, hr, min):
'Time60 constructor - takes hours and minutes'
self.hr = hr
self.min = min
def __str__(self):
return "%s:%s" % (self.hr, self.min)
__repr__ = __str__
def __add__(self, other):
return self.__class__(self.hr + other.hr,
self.min + other.min)
def __iadd__(self, other):
self.hr += other.hr
self.min += other.min
return self
输出:
8.3 迭代器
8.3.1 RandSeq
本节介绍了两个迭代器,第一个是RandSeq,给类传入一个初始序列,然后可以通过next()无穷迭代,这也是唯一的亮点。
注意点:①__iter__()方法仅返回self,这是将对象声明为迭代器的方式
②与之前一样,调用next()来迭代,得到连续的值
源代码:
#!/usr/bin/env python
from random import choice
class RandSeq(object):
def __init__(self, seq):
self.data = seq
def __iter__(self):
return self
def next(self):
return choice(self.data)
输出: # 无法结束的循环。。
8.3.2 AnyIter
第二个例子中创建了一个迭代器,并通过给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())
except StopIteration:
if self.safe:
break
else:
raise
return retval
输出:
8.4 多类型定制
创建由数字-字符对组成的的类,数字和字符记为n和s,数值类型使用整型,用[n::s]来表示。类NumStr具有几个特征:①类要对数字和字符串进行初始化,默认n=0、s=''②加法操作符把数字加起来,字符按顺序连接③乘法操作符数字相乘、字符累积④当数字为0且字符为空时,类实例布尔值为False⑤实现cmp()比较并返回适当的值
源代码:
#!/usr/bin/env python
class NumStr(object):
def __init__(self, num=0, string=''):
self.__num = num
self.__string = string
def __str__(self):
return '[%d::%r]' % (self.__num, self.__string)
__repr__ = __str__
def __add__(self, other):
if isinstance(other, self.__class__):
return self.__class__(self.__num + \
other.__num,\
self.__string + other.__string)
else:
raise TypeError, \
'Illegal argument type for built-in operation'
def __mul__(self, num):
if isinstance(num, int):
return self.__class__(self.__num * num,\
self.__string * num)
else:
raise TypeError, \
"Illegal argument type for bulit-in operation"
def __nonzero__(self):
return self.__num or len(self.__string)
def __norm_cval(self, comres):
return cmp(comres, 0)
def __cmp__(self, other):
return self.__norm_cval(
cmp(self.__num, other.__num)) + \
self.__norm_cval(
cmp(self.__string, other.__string))
输出示例:
注意点:①双下划线开始的属性属于内部私有变量,不要从外部直接访问。
②%r和%s的区别
③__nonzzero__ 覆盖此方法定义类的布尔值