设为首页 收藏本站
查看: 816|回复: 0

[经验分享] 【循序渐进学Python】8.面向对象的核心——类型(下)

[复制链接]

尚未签到

发表于 2015-4-20 09:11:08 | 显示全部楼层 |阅读模式
1 构造和初始化对象
  __init__方法是Python内建众多魔法方法(什么是魔法方法?)中最常见的一个,通过这个方法我们可以定义一个对象的初始操作。当构造函数被调用的时候的任何参数都会传递给__init__方法,然后该方法根据这些参数进行对象的初始化工作:



1 # -- coding: utf-8 --
2 class Employee(object):
3     def __init__(self,name):
4         self.name = name
5
6 e = Employee('Sunshine')
7
8 print e.name # Sunshine
  与__init__方法对应的是__del__析构方法,在对象被垃圾回收前调用,除了在进行套接字、文件IO这些非托管资源操作外,一般情况下很少会用到它。
  

1.1 正确的初始化子类型
  重写是继承机制中一个重要的内容,对于构造方法尤其如此。大多数类型的子类既要初始化自己的部分,也要调用基类的构造方法,因为保证了对象被正确的初始化,如下所示:



1 # -- coding: utf-8 --
2 class Bird(object):
3     def __init__(self):
4         self.hungry = True
5
6     def eat(self):
7         if self.hungry:
8             print 'Aaaah...'
9             self.hungry = False
10         else:
11             print 'No. thanks'
12
13 class SongBird(Bird):
14     def __init__(self):
15         self.sound = 'Squawk!'
16
17     def sing(self):
18         print self.sound
19
20 # AttributeError: 'SongBird' object has no attribute 'hungry'
21 sb = SongBird()
22 sb.eat()
  
  SongBird这个子类在调用继承于父类Bird的eat()方法时由于其自身的构造函数没有正确的初始化基类的成员hungry所有就会抛出AttributeError的异常。正确初始化父类成员的方法有两个:
  1.直接调用基类构造方法。这样修改SongBird类型的定义即可:



1 class SongBird(Bird):
2     def __init__(self):
3         Bird.__init__(self) # 调用基类构造方法
4         self.sound = 'Squawk!'
5
6     def sing(self):
7         print self.sound
  
  2.调用super函数,可以这样修改SongBird类型的定义:



1 class SongBird(Bird):
2     def __init__(self):
3         super(SongBird,self).__init__() # 使用super函数
4         self.sound = 'Squawk!'
5
6     def sing(self):
7         print self.sound
  
  一般来讲,使用第一种方式:调用未绑定的基类构造方法,是为了兼容旧的代码,如果不需要兼容旧版本的代码还是推荐使用super函数来初始化基类的成员。从上面的示例可能觉得二者差别不大,但是为什么还要出现super函数呢,因为super函数(其他它是一个类)主要是用来解决多重继承问题的,因为这牵涉到顺序查找(MRO)、重复调用等等一系列的复杂问题。为了保证我们代码的一致性,我们应该同时只使用一种风格来编写代码。
  

2. 魔法方法
  在Python中内建了很多"magic"方法这些方法和其他函数有些细微的不同:


  • 从形式上来说名字都是以双下划线(__)来开始和结束的(如:__new__)
  • 大部分的魔法方法会在特定的情况下被Python自动调用。(比如__init__方法会在对象被创建后自动调用)
  

2.1 通过魔法方法定制类型
  使用Python魔法方法最大的优势在于可以创建出自己的拥有和内置类型同样行为的类出来。例如我们可以通过重写下面的魔法方法,来为自己的类型定制自己的比较规则:


  • __eq__(self,other) —— 定义了等于的行为(=)
  • __ne__(self,other) —— 定义了不等于的行为(!=)
  • __lt__(self,other) —— 定义了小于的行为()
  • __le__(self,other) —— 定义了小于等于的行为(=)
  例如,我们可以重写定义类型的==等行为,只需要重写__eq__方法即可:



1 # -- coding: utf-8 --
2 class Employee(object):
3     def __init__(self,name):
4         self.name = name
5
6     def __eq__(self,other):
7         return self.name == other
8
9 e = Employee('Sun')
10 print e == 'Sun' # True
  Python内置的魔法方法中除了这些用于比较的魔法方法之外,还有关于操作符、类型转换以及序列相关的等等一系列的魔法方法。如果想了解更多,这里有一份很好的文档(注意文档中错别字):Python魔法方法指南
  

3. 属性
  在Python中通过访问器定义的特性被称为属性(property)。有两种定义属性的方式,首先我们来使用property函数来创建属性:



1 # --coding:utf-8--
2 class Employee(object):
3     def __init__(self):
4         self.__name = None
5
6     # set 访问器
7     def getName(self):
8         return self.__name
9
10     # get 访问器
11     def setName(self,value):
12         self.__name = value
13
14     # 析构函数
15     def delName(self):
16         del self.__name
17
18     name = property(getName,setName,delName,'set and get Name property')
19
20 c = Employee()
21 c.name = "Bob"
22 print c.name # output: Bob
  
  另一种方式就是Python 2.6 新增的语法,get/set/del函数都是使用的同一个名字:



1 # --coding:utf-8--
2 class Employee(object):
3     def __init__(self):
4         self.__name = None
5
6     def name(self):
7         return self.__name
8
9     def name(self,value):
10         self.__name=value
11
12     def name(self):
13         del self.__name
14
15 c = Employee()
16 c.name = "jobs"
17 print c.name # jobs
  

3.1 控制属性访问
  如果需要加强对属性的控制,可以使用Python为我们提供一系列的魔法方法:


  • __getattribute__(self,name):当特性name被访问时自动被调用(只能新式类中使用)
  • __getattr__(self,name):当特性name被访问且对象没有相应的特性时被自动调用
  • __setattr__(self,name):给特性name赋值时会被自动调用
  • __delattr__(self,name):当删除特性name时被自动调用
  我们知道每当属性被赋值的时候,__setattr__()就会被调用,通过它和__dict__[]的组合,我们可以通过动态为一个类创建创建特性,如下:



1 # -- coding: utf-8 --
2 class Employee(object):
3     def __init__(self,name):
4         self.name = name
5
6     # RuntimeError: maximum recursion depth exceeded
7     # #每当属性被赋值的时候,__setattr__()会被调用,这样就造成了递归调用。
8     # def __setattr__(self,name,value):
9     #   self.name = value
10
11     def __setattr__(self,name,value):
12         self.__dict__[name] = value
13
14 e = Employee("sunshine")
15 print e.name
16 e.email = "sunshine@gmail.com"
17 print e.email
  
  如果只能访问特定特性的话其实也很简单,只需要想下面 __setattr__中加个if判断即可:



1 # -- coding: utf-8 --
2 class AccessCounter(object):
3     '''一个包含计数器的控制权限的类每当值被改变时计数器会加一'''
4
5     def __init__(self, val):
6         super(AccessCounter, self).__setattr__('counter', 0)
7         super(AccessCounter, self).__setattr__('value', val)
8
9     def __setattr__(self, name, value):
10         if name == 'value':
11             # 通过调用基类的__setattr__方法可以防止递归
12             super(AccessCounter, self).__setattr__('counter', self.counter + 1)
13         # 如果不想让其他属性被访问的话,那么可以抛出 AttributeError(name) 异常
14         super(AccessCounter, self).__setattr__(name, value)
15
16     def __delattr__(self, name):
17         if name == 'value':
18             super(AccessCounter, self).__setattr__('counter', self.counter + 1)
19         super(AccessCounter, self).__delattr__(name)
20
21 c = AccessCounter(10)
22 c.value = 1
23 c.value = 2
24 c.value = 3
25 c.value = 4
26 c.value = 5
27 print c.counter # value特性的值改变了5次
  

4. 定义静态方法和类成员方法
  静态方法和类成员方法分别在创建时被装入Staficmethod类型和Classmethod类型的对象中。静态方法定义没有任何参数,能够被类本身直接调用。类方法在定义时需要名为cls的参数(类似于self参数),且能够被类本身调用。Python 2.4 为我们提供了名为:装饰器(decorators)的新语法,可以很简单的创建这两种方法:



1 class MyClass(object):
2
3     @staticmethod
4     def smeth():
5         print 'This is a static method'
6
7     @classmethod
8     def cmeth(cls):
9         print 'This is a class method of',cls
10
11 MyClass.smeth() # This is a static method
12 MyClass.cmeth() # This is a class method of
13 my = MyClass()
14 my.cmeth() # This is a class method of
15 my.smeth() # This is a static method
  

5. 协议
  协议(protocol)在Python中的概念和C#中的接口比较类似,协议说明了应该实现何种方法和这些方法应该做什么。比如说,如果想要自定义一个序列,那么只需要遵守序列的协议即可。
  

5.1 迭代器
  迭代器必须遵循迭代器协议,需要有 __iter__ (返回它本身) 和 next。__iter__方法是迭代器规则(iterator protocol)的基础。__iter__返回一个具有next方法的迭代器(iterator)对象,调用next方法时,迭代器会返回它的下一个值,直到没有值可返回时才会引发一个StopIteration异常。下面的示例使用迭代器来创建斐波那契数列,如下:



1 class Fibs(object):
2     def __init__(self):
3         self.a = 0
4         self.b = 1
5
6     def next(self):
7         self.a,self.b = self.b,self.a+self.b
8         return self.a
9
10     def __iter__(self):
11         return self
12
13 fibs = Fibs()
14
15 # output: 1 1 2 3 5 8 13 21 34 55
16 for f in fibs:
17     if f < 60:
18         print f,
19     else:
20         break
  
  我们可以借助内建函数iter从可迭代的对象中获得迭代器:



1 it = iter([1,2,3])
2 print it.next() # 1
3 print it.next() # 2
4 print it.next() # 3
  
  也可以将迭代器转换为一个序列:



1 # -- coding: utf-8 --
2 class TestIterator(object):
3     value = 0
4     def next(self):
5         self.value += 1
6         if self.value >10:raise StopIteration
7         return self.value
8
9     def __iter__(self):
10         return self
11
12
13 ti = TestIterator()
14
15 # 通过list构造方法显示将迭代器转换为列表
16 print list(ti)
  

6. 生成器
  生成器是用一种普通的函数语法定义的迭代器。任何包含yield语句的函数都可以称为生成器。当包含yield语句的函数被调用时,函数体中的代码不会被执行,而是返回一个迭代器对象。每次请求一个值,就会执行生成器中的代码,知道遇到一个yield或者return语句。如下:



1 # -- coding: utf-8 --
2 nested = [[1,2],[3,4],[5]]
3
4 def flatten(nested):
5     for sublist in nested:
6         for element in sublist:
7             yield element
8
9 #
10 print flatten(nested)
11
12 # 调用时才真正执行
13 for num in flatten(nested):
14     print num, # 1 2 3 4 5
  
  

参考资料&进一步阅读
  Python 魔法方法指南
  Python 描述符简介
  Python基础教程(第二版)
  深刻理解Python中的元类(metaclass)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-58651-1-1.html 上篇帖子: Python补充06 Python之道 下篇帖子: python翻译[排序高级]
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表