【循序渐进学Python】8.面向对象的核心——类型(下)
1 构造和初始化对象
__init__方法是Python内建众多魔法方法(什么是魔法方法?)中最常见的一个,通过这个方法我们可以定义一个对象的初始操作。当构造函数被调用的时候的任何参数都会传递给__init__方法,然后该方法根据这些参数进行对象的初始化工作:
# -- coding: utf-8 --
class Employee(object):
def __init__(self,name):
self.name = name e = Employee('Sunshine') print e.name # Sunshine
与__init__方法对应的是__del__析构方法,在对象被垃圾回收前调用,除了在进行套接字、文件IO这些非托管资源操作外,一般情况下很少会用到它。
1.1 正确的初始化子类型
重写是继承机制中一个重要的内容,对于构造方法尤其如此。大多数类型的子类既要初始化自己的部分,也要调用基类的构造方法,因为保证了对象被正确的初始化,如下所示:
# -- coding: utf-8 --
class Bird(object):
def __init__(self):
self.hungry = True def eat(self):
if self.hungry:
print 'Aaaah...'
self.hungry = False
else:
print 'No. thanks' class SongBird(Bird):
def __init__(self):
self.sound = 'Squawk!' def sing(self):
print self.sound # AttributeError: 'SongBird' object has no attribute 'hungry'
sb = SongBird()
sb.eat()
SongBird这个子类在调用继承于父类Bird的eat()方法时由于其自身的构造函数没有正确的初始化基类的成员hungry所有就会抛出AttributeError的异常。正确初始化父类成员的方法有两个:
1.直接调用基类构造方法。这样修改SongBird类型的定义即可:
class SongBird(Bird):
def __init__(self):
Bird.__init__(self) # 调用基类构造方法
self.sound = 'Squawk!' def sing(self):
print self.sound
2.调用super函数,可以这样修改SongBird类型的定义:
class SongBird(Bird):
def __init__(self):
super(SongBird,self).__init__() # 使用super函数
self.sound = 'Squawk!' def sing(self):
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)—— 定义了小于的行为(<)__gt__(self,other)—— 定义了大于的行为(>)__le__(self,other)—— 定义了小于等于的行为(<=)__ge__(self,other)—— 定义了大于等于的行为(>=)
例如,我们可以重写定义类型的==等行为,只需要重写__eq__方法即可:
# -- coding: utf-8 --
class Employee(object):
def __init__(self,name):
self.name = name def __eq__(self,other):
return self.name == other e = Employee('Sun')
print e == 'Sun' # True
Python内置的魔法方法中除了这些用于比较的魔法方法之外,还有关于操作符、类型转换以及序列相关的等等一系列的魔法方法。如果想了解更多,这里有一份很好的文档(注意文档中错别字):Python魔法方法指南
3. 属性
在Python中通过访问器定义的特性被称为属性(property)。有两种定义属性的方式,首先我们来使用property函数来创建属性:
# --coding:utf-8--
class Employee(object):
def __init__(self):
self.__name = None # set 访问器
def getName(self):
return self.__name # get 访问器
def setName(self,value):
self.__name = value # 析构函数
def delName(self):
del self.__name name = property(getName,setName,delName,'set and get Name property') c = Employee()
c.name = "Bob"
print c.name # output: Bob
另一种方式就是Python 2.6 新增的语法,get/set/del函数都是使用的同一个名字:
# --coding:utf-8--
class Employee(object):
def __init__(self):
self.__name = None def name(self):
return self.__name def name(self,value):
self.__name=value def name(self):
del self.__name c = Employee()
c.name = "jobs"
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__[]的组合,我们可以通过动态为一个类创建创建特性,如下:
# -- coding: utf-8 --
class Employee(object):
def __init__(self,name):
self.name = name # RuntimeError: maximum recursion depth exceeded
# #每当属性被赋值的时候,__setattr__()会被调用,这样就造成了递归调用。
# def __setattr__(self,name,value):
# self.name = value def __setattr__(self,name,value):
self.__dict__[name] = value e = Employee("sunshine")
print e.name
e.email = "sunshine@gmail.com"
print e.email
如果只能访问特定特性的话其实也很简单,只需要想下面 __setattr__中加个if判断即可:
# -- coding: utf-8 --
class AccessCounter(object):
'''一个包含计数器的控制权限的类每当值被改变时计数器会加一''' def __init__(self, val):
super(AccessCounter, self).__setattr__('counter', 0)
super(AccessCounter, self).__setattr__('value', val) def __setattr__(self, name, value):
if name == 'value':
# 通过调用基类的__setattr__方法可以防止递归
super(AccessCounter, self).__setattr__('counter', self.counter + 1)
# 如果不想让其他属性被访问的话,那么可以抛出 AttributeError(name) 异常
super(AccessCounter, self).__setattr__(name, value) def __delattr__(self, name):
if name == 'value':
super(AccessCounter, self).__setattr__('counter', self.counter + 1)
super(AccessCounter, self).__delattr__(name) c = AccessCounter(10)
c.value = 1
c.value = 2
c.value = 3
c.value = 4
c.value = 5
print c.counter # value特性的值改变了5次
4. 定义静态方法和类成员方法
静态方法和类成员方法分别在创建时被装入Staficmethod类型和Classmethod类型的对象中。静态方法定义没有任何参数,能够被类本身直接调用。类方法在定义时需要名为cls的参数(类似于self参数),且能够被类本身调用。Python 2.4 为我们提供了名为:装饰器(decorators)的新语法,可以很简单的创建这两种方法:
class MyClass(object):
@staticmethod
def smeth():
print 'This is a static method'
@classmethod
def cmeth(cls):
print 'This is a class method of',cls
MyClass.smeth() # This is a static method
MyClass.cmeth() # This is a class method of <class '__main__.MyClass'>
my = MyClass()
my.cmeth() # This is a class method of <class '__main__.MyClass'>
my.smeth() # This is a static method
5. 协议
协议(protocol)在Python中的概念和C#中的接口比较类似,协议说明了应该实现何种方法和这些方法应该做什么。比如说,如果想要自定义一个序列,那么只需要遵守序列的协议即可。
5.1 迭代器
迭代器必须遵循迭代器协议,需要有 __iter__ (返回它本身) 和 next。__iter__方法是迭代器规则(iterator protocol)的基础。__iter__返回一个具有next方法的迭代器(iterator)对象,调用next方法时,迭代器会返回它的下一个值,直到没有值可返回时才会引发一个StopIteration异常。下面的示例使用迭代器来创建斐波那契数列,如下:
class Fibs(object):
def __init__(self):
self.a = 0
self.b = 1 def next(self):
self.a,self.b = self.b,self.a+self.b
return self.a def __iter__(self):
return self fibs = Fibs() # output: 1 1 2 3 5 8 13 21 34 55
for f in fibs:
if f < 60:
print f,
else:
break
我们可以借助内建函数iter从可迭代的对象中获得迭代器:
it = iter([1,2,3])
print it.next() #
print it.next() #
print it.next() #
也可以将迭代器转换为一个序列:
# -- coding: utf-8 --
class TestIterator(object):
value = 0
def next(self):
self.value += 1
if self.value >10:raise StopIteration
return self.value def __iter__(self):
return self ti = TestIterator() # 通过list构造方法显示将迭代器转换为列表
print list(ti)
6. 生成器
生成器是用一种普通的函数语法定义的迭代器。任何包含yield语句的函数都可以称为生成器。当包含yield语句的函数被调用时,函数体中的代码不会被执行,而是返回一个迭代器对象。每次请求一个值,就会执行生成器中的代码,知道遇到一个yield或者return语句。如下:
# -- coding: utf-8 --
nested = [[1,2],[3,4],[5]] def flatten(nested):
for sublist in nested:
for element in sublist:
yield element # <generator object flatten at 0x0000000001D90168>
print flatten(nested) # 调用时才真正执行
for num in flatten(nested):
print num, # 1 2 3 4 5
参考资料&进一步阅读
【循序渐进学Python】8.面向对象的核心——类型(下)的更多相关文章
- 尚学python课程---11、linux环境下安装python注意
尚学python课程---11.linux环境下安装python注意 一.总结 一句话总结: 准备安装依赖包:zlib.openssl:yum install zlib* openssl*:pytho ...
- 【循序渐进学Python】7.面向对象的核心——类型(上)
我们知道Python是一门面向对象的脚本语言.从C#的角度来看:首先Python支持多继承.Python 类型成员通常都是public的,并且所有成员函数都是virtual的(可以直接重写). 1. ...
- 跟我一起学.NetCore之选项(Options)核心类型简介
前言 .NetCore中提供的选项框架,我把其理解为配置组,主要是将服务中可供配置的项提取出来,封装成一个类型:从而服务可根据应用场景进行相关配置项的设置来满足需求,其中使用了依赖注入的形式,使得更加 ...
- 【我要学python】面向对象系统学习
第一节:初识类的定义和调用 c1.py #类 = 面向对象 #类 最基本作用:封装 #类中不仅可以定义变量 还可以定义函数等等,例: class student( ): name = ' ' age ...
- 【循序渐进学Python】14.数据库的支持
纯文本只能够实现一些简单有限的功能.如果想要实现自动序列化,也可以使用 shelve 模块和 pickle 模块来实现.但是,如果想要自动的实现数据并发访问,以及更标准,更通用的数据库(databas ...
- 【循序渐进学Python】11.常用标准库
安装完Python之后,我们也同时获得了强大的Python标准库,通过使用这些标准库可以为我们节省大量的时间.这里是一些常用标准库的简单说明.更多的标准库的说明,可以参考Python文档 sys 模块 ...
- 【循序渐进学Python】9.异常处理
1. 抛出异常和自定义异常 Python用异常对象(exception object)表示异常情况,遇到错误后,会引发异常.如果异常对象并未被处理或捕捉,程序就会用所谓的回溯(Traceback,一种 ...
- 【循序渐进学Python】6.Python中的函数
1. 创建函数 一个函数代表一个行为并且返回一个结果(包括None),在Python中使用def关键字来定义一个函数,如下: def hello(name): print 'hello,' + nam ...
- 【循序渐进学Python】5.Python常用流程控制及其他语句
1. 赋值语句常用技巧 通过序列解包(sequence unpacking)可以将多个值的序列解开,让后一一放置到变量的序列中.解包的序列中的元素必须和等号左边变量数量一致.如下: values = ...
随机推荐
- MYSQL校对规则
一.前言 有时候遇到这种情况,你用一个like语句查询,查到的结果中有一些并没有包含你查询的关键词的纪录:有时候遇到这种情况,你的数据库自作聪明的大小写不敏感,让你在更新时把大小写不同的两条记录都更新 ...
- 延长Toast显示时间
---恢复内容开始--- 由于Toast的显示时间只有两种: Toast.LENGTH_SHORT: 2秒 Toast.LENGTH_LONG: 3.5秒 而且是写死的,没给开发者自定义时间的权利,所 ...
- kwm备忘
brew install homebrew/binary/kwm ==> Tapping homebrew/binary Cloning into '/usr/local/Library/Tap ...
- 用git上传本地项目到github上
首先确认自己已经安装了git,打开git bash,输入ssh-keygen -t rsa -C "自己的邮箱地址@XXX.com" ,生成自己的公钥与私钥 一路默认回车,会生 ...
- JS_Detail和Discipline
编码原则 Js控件代码3部曲 (1)设置元素的 状态 在onready中添加 (2)设置元素的 动作, 每个动作 封装成 function(enclosure) (3)remove load之前 删除 ...
- [转载]在 JavaScript 中判断“空值”
http://lync.in/check-empty-value-in-javascript/ 有时候我们会遇到这样的情况:在一些前端控件要提交数据到服务器端的数据验证过程中,需要判断提交的数据是否为 ...
- Spark源码系列(二)RDD详解
1.什么是RDD? 上一章讲了Spark提交作业的过程,这一章我们要讲RDD.简单的讲,RDD就是Spark的input,知道input是啥吧,就是输入的数据. RDD的全名是Resilient Di ...
- 11款样式新颖的 jQuery/CSS3 网页菜单
今天为大家准备了11款样式风格挺不错的jQuery/CSS3网页菜单,主要包括面包屑菜单.下拉菜单.Tab菜单等,喜欢的朋友赶紧收藏,一起来看看这些菜单. 1.jQuery / CSS3多功能下拉菜单 ...
- CSS - Select 标签在不同浏览器中的高度设置
当使用Select标签时,在不同浏览器中显示的高度不同,如何解决此问题: 解决方法链接:http://stackoverflow.com/questions/20477823/select-html- ...
- 配置ini指定eclipse启动JDK版
eclipse mars1 需要JDK 1.7+ 解决方案: 改eclipse.ini配置文件 -startupplugins/org.eclipse.equinox.launcher_1.3.100 ...