python协议_Python的对象协议

Python是一门动态语言,Duck Typing概念遍布其中,所以其中的Concept并不是以类型的约束为载体,而是使用称作为协议的概念。那什么是Duck Typing呢?

Duck Typing是鸭子类型,在动态语言中用的较多,是动态类型语言设计的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口决定,而是由当前方法和属性的集合决定。说白了就是并不关心对象是什么类型,只关心行为。

这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。

看下面例子:#coding=utf-8

class Duck:

def quack(self):

print u"嘎嘎嘎!"

def feathers(self):

print u"这是一只Duck。"

class Person:

def quack(self):

print u"hello,world!"

def feathers(self):

print u"这是人。"

def in_the_forest(duck):

duck.quack()

duck.feathers()

def game():

donald = Duck()

john = Person()

in_the_forest(donald)

in_the_forest(john)

game()

运行结果不用贴出来,都知道是什么。那么上面代码从何体现出Duck Typing风格呢?

看看in_the_forest()函数,它对它的参数只有两个要求,那就是这个参数必须要有quack()和feathers()两个方法,不管它是什么类型,只要有这两个方法,都可以作为in_the_forest()函数的参数,而类Duck和Person都实现了这个方法,所以它们的实例都可以作为in_the_forest()的参数。这个就是鸭子类型,不关注对象类型本身,只关心行为。这一点更C++还是有很大区别的,我也学习过C++,也了解C++多态性以及虚函数所以感觉这差别还是蛮大的。

现在来看看什么是协议吧,简单的说,在python中我需要调用你的某个方法,你正好有这个方法,这就是协议,比如在加法运算中,当出现加号(+)时,那么按照数值类型相关的协议,python会自动去调用相应对象的__add__()方法,这就是协议。

python中有很多协议,比如下面代码:#coding=utf-8

a = 1

print dir(a)

结果如下:

['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', '__format__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__', '__index__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__mul__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'imag', 'numerator', 'real']

上面代码中,a是整数对象,我们列出来该对象的所有属性,上面的属性列表中,以"__"开头并且以"__"结尾的基本上都是对象协议依赖的方法。是不是很多呀,我们可以将它们分成以下几类:

(1)类型转换协议

类型转换协议,顾名思义就是用来进行类型转的咯,比如a=1,a是整型,想把它变成float浮点型,我们可以这样:#coding=utf-8

a = 1

print type(a)

b = a.__float__()

print b

print type(b)

当然你也可以用内置函数float()。

除了__float__()外,还有__str__()、__repr__()、__int__()、__long__()、__nonzero__()等等,这些都是类型转换协议依赖的方法。

在这里提下__nonzero__(),他用t于将类转换为布尔值。通常在用类进行判断和将类转换成布尔值时调用。通常会在if等条件语句会调用该方法。比如:#coding=utf-8

list1 = []

if list1:

print "1"

上面代码在执行过程中,先会调用list1对象的__nonzero__()方法,判读它是不是空,是的话就会转换为False。假设有些对类没有定义__nonzero__()方法,那么将如何判断真假呢?此时对象会获取__len__()方法,如果该方法返回值为0,则表示为假。

如果一个类即没有定义__nonzero__()方法,也没有定义__len__()方法,那么该类的所有实例用if判断的话全为真。

比如看下面例子:#coding=utf-8

class A:

def __nonzero__(self):

print u"定义了__nonzero__()方法"

return 1

class B:

def __len__(self):

print u"没定义__nonzero__()方法,但定义了__len__()方法"

return 1

class C:

pass

if A():

a = 1

print a

if B():

a = 2

print a

if C():

a = 3

print a

运行结果如下:

希望上面的例子能够帮助你理解部分知识。

(2)用以比较大小的协议

这个协议依赖于__cmp__()方法,当两者相等时返回0,self等操作符进行重载支撑的机制,也就是说重载这些操作符,就是要重新定义对应的方法。

(3)与数值类型相关的协议

这一类方法比较多,主要是数值之间的一些操作。分类方法操作符/函数说明

数值运算符__add__+加

__sub__-减

__mul__*乘

__div__/除

__floordiv__//整除

__truediv__/真除法

__pow__**幂运算

__mod__%取余

__divmod__divmod()余、除

位运算符__lshift__<

__rshift__>>向右移位

__and__&与

__or__|或

__xor__^异或

__invert__~非

运算赋值符__iadd__+=

__isub__-=

__imul__*=

__idiv__/=

__ifloordiv__//=

__itruediv__/=

__ipow__**=

__imod__%=

__ilshift__<<=

__irshift__>>=

__iand__&=

__ior__|=

__ixor__^=

其它__pos__+正

__neg__-负

__abs__abs()绝对值

举个例子,比如C++中,"<

class endl(object):

pass

class Cout(object):

def __lshift__(self, other):

if other is endl:

print

return

print other

return self

cout = Cout()

cout <

运行结果如下:

不过在python中,将"<

另外Python中还有一个特有的概念:反运算。例如a + b,调用的是a的__add__()方法,如果a没有__add__()方法,但b有,可是这种写法调用b.__add__()又不对的,这时候Python有了一个反运算协议,它会去检测b中有没有__radd__(),如果有的话,那么a为参数调用之。类似__radd__()方法,所有数值运算和位运算都之处,规则一律在前面加前缀r。

(4)容器类协议

既然为容器,那么肯定会有查询、取值、删除、赋值、求长度等等一系列动作行为,那么必有对应的方法与这些操作对应。打印出dir(list)的值,结果如下:

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

其中:

求长度,也就是查询容器内有多少个对象,用__len__()方法,内置函数len()就是通过该方法实现的。

取值、删除、赋值依次对应__getitem__()、__delitem__()、__setitem__(),有些容器没有__setitem__(),比如字符串,应为字符串是不可变对象

__iter__()实现了迭代协议,__reversed__()则提供了对内置函数reversed()的支持,用来排序。

还有成员关系判定符in和not in,对应的方法是__contains__()

(5)可调用对象协议

可调用对象,也就是类似函数对象,能够让类实例表现的像函数一样,这样可以让每一个函数调用都有所不同。怎么理解这句话呢?还是看例子吧。#coding=utf-8

class A(object):

def __init__(self,name):

self.name = name

def __call__(self):

print "dongn something with %s"%(self.name)

a = A('li lei')

b = A('han ×××')

print a()

print b()

运行结果如下:

看看上面的例子,我们能够像调用函数一样调用实例

对象通过提供__call__(slef, [,*args [,**kwargs]])方法可以模拟函数的行为,如果一个对象x提供了该方法,就可以像函数一样使用它,也就是说x(arg1, arg2...) 等同于调用x.__call__(self, arg1, arg2) 。

(6)哈希协议

如果对象有__hash__()方法,表示是一个可哈希对象。__hash__()方法支持这hash()这个内置函数。按照文档里面的解释“如果一个对象是可哈希的,那么在它的生存期内必须不可变(需要一个哈希函数),而且可以和其他对象比较(需要比较方法).比较值相同的对象一定有相同的哈希值”。

这也就是说所有不可变的内置类型t都是可哈希的,比如string,tuple。所有可变的内置类型都是不可哈希的,比如list,dict(即没有__hash__()方法)。字典的key必须是可哈希的,所以tuple,string可以做key,而list不能做key。

(7)上下文管理器协议

这个在之前的博文中讲过,在此不做过多解释,可参考之前的相关博文。http://11026142.blog.51cto.com/11016142/1845862

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值