Scalers点评:在2015年,ScalersTalk成长会完成Python小组完成了《Python核心编程》第1轮的学习。到2016年,我们开始第二轮的学习,并且将重点放在章节的习题上。Python小组是成长会内部小组,如果你想和我们一起学习Python,你需要是成长会成员,并且完成相关进群任务。
我们的节奏是行动是一周一章学到位,章节习题都会一个一个过。通过到位地执行,达到实质性的提升。
往期日志:
本周学习情况
本周(20160412-20160417)学习第十三章,章节内容为《面向对象编程》,本周复盘主持人为shawn。
本章主要内容
本章首先讲解了OOP的基本概念、类和实例的特性、类的继承定制机器其他高级特性等
本章主要知识点
1.面向对象与面向过程编程
程序 = 算法 + 数据结构
面向过程 = 行为 + 数据
面向对象 = 对象 + 对象(对象封装了行为和数据)
常用术语:
抽象/实现 封装/接口 合成 派生/继承/继承结构 泛化/特化 多态
自省/反射 dir,type, __dict__,__name__
2.老式类 vs 新式类
老式类:
class oldAddrBookEntry: 'address book entry class' def __init__(self, nm, ph): # 定义构造器 self.name = nm # 设置 name self.phone = ph # 设置 phone print 'Created ioldAddrBookEntry nstance for:', self.name def updatePhone(self, newph): # 定义方法 self.phone = newph print 'Updated phone# for:', self.name
新式类:新式类必须继承一个父类。
class AddrBookEntry(object): 'address book entry class' def __init__(self, nm, ph): # 定义构造器 self.name = nm # 设置 name self.phone = ph # 设置 phone print 'Created instance for:', self.name def updatePhone(self, newph): # 定义方法 self.phone = newph print 'Updated phone# for:', self.name
创建新式类和旧式类对象:
class Test(unittest.TestCase): def testAddrBookCreated(self): addrBook = AddrBookEntry('wang1','123456') addrBook.updatePhone('456789') def testOldAddrBookEntry(self): old = oldAddrBookEntry('li1','1111')
旧式类没有__class 属性
class M: bar = 5 dir(M) ['__doc__', '__module__', 'bar']
3.类声明和定义
python函数声明和定义是同时进行的,python要强制程序员在子类中定义方法只能够通过抛出NotImplementedError异常来实现,因为python不支持java中抽象类和c++中纯虚函数。
4.类属性
类属性:数据属性仅仅是所定义的类的变量。
方法:python 严格要求,没有实例,方法是不能被调用的。这种限制即绑定概念(binding)
查看类属性:
定义如下类:
class C(object): foo = 100
C.foo = C.foo +1 101
通过dir显示C的属性:通过列表显示类属性
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'foo']
__dict返回的是一个类属性字典
, '__doc__': None, '__module__': '__main__', '__weakref__': , 'foo': 101}>
其中看到foo 是自定义的类属性。
5.特殊的类属性
__doc__及__module__,是所有类都具备的特殊类属性(另外还有__dict)
C.__name__ 类C的名字(字符串) C.__doc__ 类C的文档字符串 C.__bases__ 类C的所有父类构成的元组 C.__dict__ 类C的属性 C.__module__ 类C定义所在的模块 C.__class__ 实例C对应的类(仅新式类中)
6.实例
实例是有生命的类。实例是那些主要用在运行期时的对 象,类被实例化得到实例,该实例的类型就是这个被实例化的类。
实例的生命周期:类对象、__init、__”构造器”方法、__new__“构造器”方法、__del“解析器”方法。
跟踪实例
class InstCt(object): count = 0 # count is class attr count 是一个类属性 def __init__(self): # increment count 增加 count InstCt.count += 1 def __del__(self): # decrement count 减少 count InstCt.count -= 1 def howMany(self): # return count 返回 count return InstCt.count ic = InstCt ic1 = InstCt \#ic.howMany ##此调用会出错,howMany必须绑定到对象上。 \####方法必须绑定到实例上: InstCt.howMany 输出结果为:2
实例上动态添加属性的例子
ic.b = 5 ##动态添加实例属性 c.b ic.b \#输出 5 ic.__dict__ \##实例的字典属性中仅有实例属性,没有类属性或特殊属性。 \#输出 {'b': 5} --count dir(ic) \输出 ['__class__', '__del__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'b', ##动态添加的实例属性显示了 'count', 'howMany'] In [31]: ic.__class__ \输出 __class__属性会显示出package.classname全路径 __main__.InstCt ##class属性
内建类型属性
x = 3+0.14j x.__class__ complex x.__dict__ ##内建类型没有 __dict__属性 AttributeError: 'complex' object has no attribute '__dict__'
7.实例属性与类属性关系
实例属性会覆盖类属性,实例属性初始值为类属性
不可变修改实例属性不影响类属性
可变类型实例属性影响类属性
可变类型属性影响类属性的例子
class Boo1(object): x = {2003: 'poe2'} \#打印类属性 Boo1.x {2003: 'poe2'} \#修改实例属性 b2.x[2006] = 'poe6' \#查看实例属性 {2003: 'poe2', 2006: 'poe6'} \#查看类属性-类属性被修改了(被b2对象修改了) Boo1.x {2003: 'poe2', 2006: 'poe6'}
8.绑定和非绑定方法
python中方法都必须被绑定才能被调用,除了一些特殊场景如下:子类调用父类的构造函数(构造函数阶段对象还没有生成无法调用)
class EmplAddrBookEntry(AddrBookEntry): 'Employee Address Book Entry class' # 员工地址记录条目 def __init__(self, nm, ph, em): AddrBookEntry.__init__(self, nm, ph) self.empid = id self.email = em
9.采用修饰符的方式实现静态方法和类方法
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)
10.继承
base 属性任何一个类都有包含父类的元组集合,python中通过__base属性来维护父类列表。
通过继承来覆盖方法如果子类重写了父类的方法那么方法调用通过子类调用该方法会是子类重写了的方法。
class P(object): def foo(self): print 'Hi, I am P-foo' p = P >>> p.foo Hi, I am P-foo \#现在来创建子类 C,从父类 P 派生: class C(P): def foo(self): print 'Hi, I am C-foo' >>> c = C >>> c.foo Hi, I am C-foo \#调用未被绑定的基类方法 >>> P.foo(c) Hi, I am P-foo \#子类方法中显示的调用基类方法 class C(P): def foo(self): P.foo(self)#可以改成 super(C,self).foo print 'Hi, I am C-foo'
__doc 属性是类、模块
谈一谈构造函数重写
构造重写:
构造函数重写:java中子类会自动调用父类的构造函数。
C++需要在子类构造函数中声明
class animal { public: animal(int height, int weight) { cout<<"animal construct"<
python 调用父类构造函数需要显示的调用
class C(P): def __init__(self): P.__init__(self) print "calling C's constructor"
10.从标准类派生
class RoundFloat(float): def __new__(cls, val): ######################使用 super内建函数去捕获对应的父类以调用它的__new__方法 return super(RoundFloat, cls).__new__(cls, round(val, 2)) \#运行结果 r = RoundFloat(2.5678) r输出为2.57
11.多重继承
类、实例和其他对象的内建函数
issubclass:判断一个类是否是另一个类的子类
isinstance:判断对象是否是另一个给定类的实例或者是其子类的实例时候返回true
hasattr、getattr、setattr、delattr:在操作 obj.attr 时,就相当于调用*attr(obj,’attr’….)系列函数
vars
class C(object): pass >>> c = C >>> c.foo = 100 >>> c.bar = 'Python' >>> c.__dict__ {'foo': 100, 'bar': 'Python'} >>> vars(c) {'foo': 100, 'bar': 'Python'}
MRO 方法解释顺序
老式类使用的是深度优先的解释顺序。
新式类使用的是广度优先的解释顺序,新式类会有菱形结构(不能采用深度优先会一直调用objec的方法),所以实践策略是老式类采用深度优先、新式类采用广度优先。
12.用特殊方法定制类
简单的定制
class RoundFloatManual(object): def __init__(self, val): \#它断言了传递给构造器的参数类型必须为一个浮点数 assert isinstance(val, float), "Value must be a float!" self.value = round(val, 2) def __str__(self):
数值定制:定制加法
class Time60(object): # ordered pair 顺序对 def __init__(self, hr, min): # constructor 构造器 self.hr = hr # assign hours 给小时赋值 self.min = min # assign minutes 给分赋值 def __str__(self): return str(self.hr) + ":" + str(self.min) def __add__(self,other): return self.__class__(self.hr + other.hr,other.hr + other.min) \# 输出结果 tim = Time60(2,3) tim2 = Time60(4,12) tim3 = tim + tim2 print tim3 ##6:16 print id(tim) print id(tim2) print id(tim3) \#输出的id不一样 4357632208 4357632336 4357633360
迭代器定制
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 RuntimeError('not safe') raise return retval \# 运行结果 \#安全模式在项失控出现前得到迭代器所得到的元素 any4 = AnyIter([1,2,3,4,5],True) In [42]: 14 any4.next(14) Out[42]: [1, 2, 3, 4, 5] \#非安全模式 any3 = AnyIter([1,2,3,4,5]) any3.next(14) 会抛出如下错: --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) in ----> 1 any3.next(14) in next(self, howmany) 12 for eachItem in range(howmany): 13 try: ---> 14 retval.append(self.iter.next) 15 except StopIteration: 16 if self.safe: StopIteration:
13.私有化
双下划线双下划线属性在运行时会“混淆”所以直接访问时不允许的,实际会在属性前面加上 下划线+类名+属性名 比如__num__属性变成 __self._numstr__num。
单下划线模块级私有化。
14.授权
包装包装定义: “包装”是python中经常用到的一个术语。它是一个通用的名字,意思是对一个已存在对象进行包装,不管它是数据类型,还是一段代码,可以是对一个已存在的对象,增加新的,删除不要的,或者膝盖其他已存在的功能。
授权
a).授权的过程,即是所有 更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
b).实现授权的关键点就是覆盖__getattr__方法,在代码中包含一个对 __getattr内建函数的调用,调用 __getattr以得到默认对象属性(数据属性或者方法)并返回它以便访问或调用。
c).getattr__的工作方式是:当搜索一个属性时,任何局部对象首先被找到(定制的对象)。 如果搜索失败了,则__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) \输出结果 wm = WrapMe(3+4j) wm.real >>>3.0 wm2 = WrapMe([1,2,3,4]) wm2.count(2) >>>1 wm2[3] \--------------------------------------------------------------------------- TypeError Traceback (most recent call last) in ----> 1 wm2[3] TypeError: 'WrapMe' object does not support indexing \# 切片操作符是序列类型的一部分,而不是通过 \_\_getItem\_\_特殊方法实现的。 \#解决办法 wm2.get[2] 3 In [9]: type(wm2) __main__.WrapMe \#查看w2.get的类型 type(wm2.get) list
15.新式类的通用特性
内建函数转换为了工厂函数
int, long, float, complex str, unicode list, tuple type
加入了新的函数管理散兵
basestring1 dict bool set,2 frozenset2 object classmethod staticmethod super property file
注意:尽管 isinstance很灵活,但它没有执行“严格匹配”比较——如果 obj 是一个给定类 型的实例或其子类的实例,也会返回 True。但如果想进行严格匹配,你仍然需要使用 is 操作符。
16.slots类属性__slots是一个类变量,由一序列型对象组成,由所有合法标识构成的实例属性的集 合来表示,使用__slots类变量解决__dict占用内存过多的问题。
17.描述符
基础概念
描述符可以认为是对象属性的一个代理,这样当你访问属性时可以通过描述符或者采用常规的方式来访问它,任何实现了描述符协议的类都可以叫做描述符。
描述符协议: __get__, __set__, 和 __delete__
方法描述符:实现了__set__方法。
数据描述符:同事覆盖实现了__get__及其__set__的类称作数据描述符。
__getAttribute__整个描述符系统的心脏是__getAttrbute__。
描述符的优先级
类属性: 描述符是一个类的属性,所以所有的类属性都有最高优先级。
数据描述符: 被__get__,__set__实现的描述符
实例属性: __dict
非数据描述符: 提供一个值(如果还不是一个对象属性的话)
默认为__getattr__ :非数据描述符没有找到,__getattribute__引发一个attributeError,__getattr__在attributeError抛出给用户之前被调用。
描述符示例
通过描述符禁止对对属性访问和赋值
示例1:
class Dev1(object): def __get__(self, obj, typ=None): pass def __set__(self, obj, val): pass class C1(object): foo = Dev1 c11 = C1 c11.foo = 'foo' c11.foo \#属性值为空 ##数据描述符比实例描述符优先级高
示例2:
class Dev3(object): def __init__(self, name=None): self.name = name def __get__(self, obj, typ=None): print 'Accessing [%s]... ignoring' %(self.name) def __set__(self, obj, val): print 'Assigning %r to [%s]... ignoring' % (val, self.name) class C3(object): foo = Dev3('foo') c3 = C3 x = c3.foo Accessing [foo]... ignoring c3.\_\_dict\_\_['foo']\#没有设置值去访问就是错的。 \--------------------------------------------------------------------------- KeyError Traceback (most recent call last) in ----> 1 c3.__dict__['foo'] KeyError: 'foo' c3.\_\_dict\_\_['foo'] = 'bar' c3.__dict__['foo'] 'bar'
示例3:只提供__set操作符
class Dev4(object): def __init__(self, name=None): self.name = name def __set__(self, obj, val): print 'Assigning %r to [%s]... ignoring' % (val, self.name) class C4(object): foo = Dev4('foo') c4 = C4 y = c4.foo c4.foo = 'bar4' Assigning 'bar4' to [foo]... ignoring c4.foo <\_\_main\_\_.Dev4 at 0x103bbc210> \#有输出因为没有定义 \_\_get\_\_方法。
示例4:实例属性大于非数据属性,比如实例属性会覆盖类属性
\>>> class FooFoo(object) def foo(self): print 'Very important foo method.' \>>> bar = FooFoo \>>> bar.foo Very important foo method. \>>> \>>> bar.foo = 'It is no longer here.' \>>> bar.foo 'It is no longer here.' \>>> \>>> del bar.foo ##删除实例属性后,可以调用到类属性了。 \>>> bar.foo Very important foo method.
18.Metaclasses
元类:让你来定义某些类是如何创建的,在执行类定义时,解释器必须要知道这个类的正确的元类。
解释器会先寻找类属性__metaclass__,如果此属性存在,就将这个属性赋值给此类作为它的元类。如果此属性没有定义,它会向上查找父类中的__metaclass__.
谁在使用元类:元类的使用者不是用户,而是程序员自己。使用元类的例子,强制校验类必须定义__str方法。
\#元类测试 class ReqStrSugRepr(type): def __init__(cls,name,bases,attrd): super(ReqStrSugRepr,cls).__init__(name,bases,attrd) if '\_\_str\_\_' not in attrd: ##强制校验类必须定义\_\_str\_\_ raise TypeError("Class requires overriding of __str__") if'__repr__' not in attrd: warn('Class suggests overriding of __repr__\n',stacklevel=3) class FooBar(object): __metaclass__ = ReqStrSugRepr #ReqStrSugRepr必须定义 __str__类 def __str__(self, *args, **kwargs): ##如果没有定义此方法会抛出异常的 return object.__str__(self, *args, **kwargs)
10.相关模块和文档
与类相关的模块
UserList 供一个列表对象的封装类
UserDict 供一个字典对象的封装类
UserString 供一个字符串对象的封装类;它又包括一个 MutableString 子类,如果有需要,可以 供有关功能
types 定义所有Python对象的类型在标准Python解释器中的名字
operator 标准操作符的函数接口
ScalersTalkID:scalerstalk
本微信公众号作者Scalers,游走在口译世界的IT从业者。微信公众号ScalersTalk,微博@Scalers,网站ScalersTalk.com,口译100小时训练计划群C 456036104
成长会是由Scalers发起的面向成长、实践行动,且凝聚了来自全球各地各行各业从业者的社群。有意入会者请和Scalers直接联系,我和其他会员会和你直接交流关于成长行动等各方面的经验教训。2016年成长会持续招募中,参见做能说会写的持续行动者:ScalersTalk成长会2016年会员计划介绍(2016.3更新)
本文暂时没有评论,来添加一个吧(●'◡'●)