知乎:https://www.zhihu.com/people/monkey.d.luffy Android高级开发交流群2: 752871516
全部博文(315)
分类: Python/Ruby
2012-02-22 21:38:24
第13章主要就是类了,看了该抽时间学习C++了,如果C++学习好了,我想来学习这个可能要好理解多了,以前学习的只是皮毛;但是看这章还是飘的很快。管它先看一遍吧,不不然没时间了,马上要去公司实习了。
类就是相关的格式,构造,析构(python叫解构),继承,覆盖这些都是一样的,只是语法不同而已。还有一些很嚼舌的概念,类的属性,实例的属性,以及它们的区别,当然还有很多其他的,我记不住了。这章理论偏多了些,理解要多了些:
13. 1 以下是一个计算旅馆收费记账的类实例;
#file: hotel.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
class HotelRoomCalc(object):
def __init__(self, rt, sales=0.085, rm=0.1): #覆盖__init__,记住无须返回值,否则无法达到统一(都是返回一个实例对象)
'''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):
#这个daily的计算不是很清除,发现课本写法我试了不太对,再研究!后来看见这个return float.__new__(cls, round(val, 2)),知道了round是为了保留位数而做的。
daily = round((self.roomRate * 14 * (1 + self.roomTax + self.salesTax)), 2)
return float(days) * daily
if __name__ == "__main__":
sfo = HotelRoomCalc(299) #对应rt, python根据参数位置来识别self自动的,不需要作者传入(省略参数)
print sfo.calcTotal()
print sfo.calcTotal(2)
sea = HotelRoomCalc(189, 0.086, 0.058) #覆盖默认参数
print sea.calcTotal()
print sea.calcTotal(4)
13. 2 小技巧(如果你在学习中需要某个类,或者方法,或者内建类型,你又不太清楚,可以用下列方法查查)
>>> x = 3.0 + 4j
>>> dir(x)
['__abs__', '__add__', '__class__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__int__', '__le__', '__long__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__nonzero__', '__pos__', '__pow__', '__radd__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rmod__', '__rmul__', '__rpow__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', 'conjugate', 'imag', 'real']
>>> [type(getattr(x, i)) for i in ('conjugate', 'imag',
... 'real')]
[
>>> x.imag
4.0
>>> x.conjugate()
(3-4j)
编程中还是挺实用的。
13.6.5 实例属性 vs 类属性
From:
核心提示:使用类属性来修改自身(不是实例属性)
正如上面所看到的那样,使用实例属性来试着修改类属性是很危险的。原因在于实例拥有它们自已的属性集,在 Python 中没有明确的方法来指示你想要修改同名的类属性,比如,没有 global关键字可以用来在一个函数中设置一个全局变量(来代替同名的局部变量)。修改类属性需要使用类名,而不是实例名。
理解:
看完这小节,还算是有点理解。大家都知道,我们用类来创建一个实例,那么我们的初始属性当然就是类属性了,当我们一旦创建实例后,我们的实例就可以独立“称王”了。我们可以用实例去做我们想做的事情,包括修改实例中的本来属性(这个属性值刚开始是类属性值);但是我们的类不会受到影响,我们依然可以用我们的类去实例化n多的类实例。
我们在改变实例的属性的时候,其实是创建了一个新的实例,我们的类属性依然如故。所以类中的这些属性就像是静态成员一样,无论外界如何改变,其本身一样不变。也就是实例属性在修改成员变量的值的时候,会有效的“隐藏”类属性,如果我们删除(del foo.x)的时候,又可以访问类属性了(print foo.x)。
列子:
Class Foo(object):
def __init__(self):
self.x = 23
def __del__(self):
pass
>>foo = Foo()
>>Foo.x #类属性23
>>foo.x #类属性23
>>foo.x = 2 #创建一个新的实例,隐藏类属性
>>Foo.x #类属性依旧不变23
>>foo.x #实例属性2
>>del foo.x #删除实例属性
>>foo.x #类属性(又重见天日)23
以上只是简单实例,当然不是说类成员变量就不可变。如果是字典的话还是可以通过修改实例来更新类属性的;
比如:
Class Foo(object):
def __init__(self):
x = {2003: 'poe2'}
def __del__(self):
pass
>>foo = Foo()
>>foo.x[2004] = ‘jane’
>>Foo.x # {2003:’poe2’, 2004:’jane’}
>>del foo.x[2004] # 这是不可以的,因为你的实例不能去删除一个类,就是你的创造者
13.3 self VS this
From:
核心笔记:self 是什么?
self 变量用于在类实例方法中引用方法所绑定的实例。因为方法的实例在任何方法调用中总是作为第一个参数传递的,self 被选中用来代表实例。你必须在方法声明中放上 self(你可能已经注意到了这点),但可以在方法中不使用实例(self)。如果你的方法中没有用到 self , 那么请考虑创建一个常规函数,除非你有特别的原因(Me:比如你可以创建一个全局函数,然后在类中调用申明它,但这样做不好;你不需要包装,干嘛还要放在类中,我们用类就是想实现包装,代码重用等重大功能)。毕竟,你的方法代码没有使用实例,没有与类关联其功能,这使得它看起来更像一个常规函数。在其它面向对象语言中,self 可能被称为 this。
Attention:只有在实例创建后,方法才是绑定的,不然就是未绑定的;只有绑定了的方法才可以通过实例来调用。
Latter:我们可以调用非绑定的方法,这是怎么回事;知道继承就还是明白点;
class EmplAddrBookEntry(AddrBookEntry):
'Employee Address Book Entry class' # 员工地址记录条目
def __init__(self, nm, ph, em):
AddrBookEntry.__init__(self, nm, ph) #这个地方就是调用未实例化的AddrBookEntry的地方;说法(既然我们的子类继承父类,然后__init__又是如此相似,我们在实例化EmpladdrBookEntry的时候就用子类的来替代好了,似乎工作机制是这样的!
self.empid = id
self.email = em
13.4 staticmethod()和 classmethod()内建函数
这些方法是可以直接通过类调用或者实例调用的,用法:
class TestStaticMethod:
def foo():
print 'calling static method foo()'
foo = staticmethod(foo)
class TestClassMethod:
def foo(cls):
print 'calling class method foo()'
print 'foo() is part of class:', cls.__name__
foo = classmethod(foo)
改进:(使用函数修饰符)
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__
13.11.2 通过继承覆盖(Overriding)方法
Example:
class P(object):
def foo(self):
print 'Hi, I am P-foo()'
现在来创建子类 C,从父类 P 派生:
class C(P):
def foo(self):
print 'Hi, I am C-foo()'
如何调用基类方法:
1》
>>> P.foo(c)
Hi, I am P-foo()
2》
class C(P):
def foo(self):
P.foo(self)
print 'Hi, I am C-foo()'
3》
class C(P):
def foo(self):
super(C, self).foo() #像java的模式,这样好处是你可以不必去回想我的基类名称到底是什么了??
print 'Hi, I am C-foo()'
13.11.3 从标准类型派生
不可变类型的例子
class RoundFloat(float):
def __new__(cls, val): #覆盖__new__
return float.__new__(cls, round(val, 2))
class RoundFloat(float):
def __new__(cls, val): #cls类似this, super用法和前面类似
return super(RoundFloat, cls).__new__(cls, round(val, 2))
可变类型的例子(你不需改变很多东西,只希望拿来就用,这样你就不需要__new__或者__init__)
class SortedKeyDict(dict):
def keys(self):
return sorted(super( SortedKeyDict, self).keys())
13.5 方法解释顺序(MRO—method resolve order)
涉及到深度优先和广度优先去搜索父类、子类、孙子类等等的解析顺序,确定最终调用的是那个类的方法或者变量值…….其实还是好理解的。至于新式类或者经典类的继承相关矛盾和问题,就看开发者如何处理了。
经典类,使用深度优先算法。因为新式类继承自 object,新的菱形类继承结构出现,问题也就接着而来了,所以必须新建一个 MRO。但某时候广度更符合开发者要求了。
后面还有很多关于定制,迭代器,包装(实现授权)的概念,描述符,元类,应该会头晕,很难理解。
中级定制:
#!/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):
'Time60 - string representation'
return '%d:%d' % (self.hr, self.min)
__repr__ = __str__ #这样__repr__和__str__就具有一样的功能了
def __add__(self, other):
'Time60 - overloading the addition operator'
return self.__class__(self.hr + other.hr,self.min + other.min)
def __iadd__(self, other):
'Time60 - overloading in-place addition'
self.hr += other.hr
self.min += other.min
return self