【循序渐进学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 = ...
随机推荐
- javascript - 二叉树
都是些简单的东西,所以直接上代码了. /** * Created by huangjacky on 14-10-3. */ function Node(element, left, right) { ...
- 获取枚举类型的描述description
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; usin ...
- 笔记《Hbase 权威指南》
为什么要用Hbase- Hbase的诞生是因为现有的关系型数据库已经无法在硬件上满足疯狂增长的数据了,而且因为需要实时的数据提取Memcached也无法满足- Hbase适合于无结构或半结构化数据,适 ...
- 读Java 804 - Quick refresher
Upcast永远是成功的,但Downcast不是,记得做instanceof判断 仅抛不同异常,而返回值相同的重载是不可以的 static import只会import静态类 static metho ...
- CvMat 矩阵的使用方法和简单程序
一:CvMat* cvInitMatHeader( CvMat* mat, int rows, int cols, int type,void* data=NULL, int step=CV_AUTO ...
- mac 自动配置java版本
首先输入命令:vi .bash_profile ,添加如下内容: # Mac默认 JDK (Mac默认自带了一个jdk6版本) export JAVA_6_HOME=`/usr/libexec/jav ...
- js 与或运算符 || && 妙用
js 与或运算符 || && 妙用,可用于精简代码,降低程序的可读性. 首先出个题: 如图: 假设对成长速度显示规定如下: 成长速度为5显示1个箭头: 成长速度为10显示2个箭头: ...
- dissmiss a UISearchBar with an SearchBarController
If you want do dissmiss a UISearchBar with an SearchBarController, just use this Code: [self.searchD ...
- SSAS:菜鸟笔记(二)定义计算(DMX脚本)
基本概念 Calculation可以定义计算成员.名称集以及执行其他脚本命令来扩展分析服务立方(Analysis Service Cube)的功能. Calculation包含MDX以及脚本两个部分: ...
- [转]SharePoint开发中可能用到的各种Context(上下文)
SharePoint是一个B/S结构的产品,所以在开发过程中会使用到各种各样的上下文(Context)信息,借此机会来总结一下.特别是Javascript的Ctx非常实用,这里记录一下! 一.Http ...