Python作为一种多范式语言,它的很多语言特性都能从其他语言上找到参照,但是Python依然形成了一套自己的“Python //代码效果参考:http://www.jhylw.com.cn/240229917.html
风格”(Pythonic)。这种Pythonic风格完全体现在 Python 的数据模型上,而数据模型中的元接口(指那些名字以两个下划线开头,以两个下划线结尾的特殊方法,例如 getitem),就是编写地道的Python代码的秘密所在。这种基于元接口实现的设计模式,也叫鸭子类型(duck typing)。鸭子类型指的是对象的类型无关紧要,只要实现了特定的接口即可。忽略对象的真正类型,转而关注对象有没有实现所需的方法、签名和语义。Python的数据模型都支持鸭子类型,鸭子类型也是地道Python编程鼓励的风格,所以如果觉得自己想创建新的抽象基类,先试着通过常规的鸭子类型来解决问题。
数据模型其实是对 Python 框架的描述,它规范了这门语言自身构建模块的接口,这些模块包括类、函数、序列、迭代器、上下文管理器等。
类
得益于 Python 数据模型,自定义类的行为可以像内置类型那样自然。实现如此自然的行为,靠的不是继承,而是元接口。Python给类设计了大量的元接口,具体请参看Python 语言参考手册中的“Data Model”章节。下面是一些类的元接口的展示。
"""
]> v1 = Vector2d(3, 4)
通过元接口iter支持拆包
]> x, y = v1
]> x, y
(3.0, 4.0)
通过元接口repr支持字面量表示和repr函数
]> v1
Vector2d(3.0, 4.0)
]> v1_clone = eval(repr(v1))
]> v1 == v1_clone
True
通过元接口str支持print函数
]> print(v1)
(3.0, 4.0)
通过元接口bytes支持bytes函数
]> octets = bytes(v1)
]> octets
b'd\x00\x00\x00\x00\x00\x00\x08@\x00\x00\x00\x00\x00\x00\x10@'
通过元接口abs支持abs函数
]> abs(v1)
5.0
通过元接口bool支持bool函数
]> bool(v1), bool(Vector2d(0, 0))
(True, False)
通过property支持可读属性
]> v1.x, v1.y
(3.0, 4.0)
]> v1.x = 123
Traceback (most recent call last):
...
AttributeError: can't set attribute
通过hash支持对象可散列,支持dict、set等函数
]> hash(v1)
7
]> set(v1)
{3.0, 4.0}
]> {v1: 'point1'}
{Vector2d(3.0, 4.0): 'point1'}
"""
from array import array
import math
class Vector2d:
typecode = 'd'
def init(self, x, y):
self.x = float(x)
self.y = float(y)
@property
def x(self):
return self.x
@property
def y(self):
return self.y
def iter(self):
return (i for i in (self.x, self.y))
def repr(self):
class_name = type(self).name
return '{}({!r}, {!r})'.format(class_name, *self)
def str(self):
return str(tuple(self))
def bytes(self):
return (bytes(【ord(self.typecode)】) +
bytes(array(self.typecode, self)))
def eq(self, other):
return tuple(self) == tuple(other)
def hash(self):
return hash(self.x) ^ hash(self.y)
def abs(self):
return math.hypot(self.x, self.y)
def bool(self):
return bool(abs(self))
函数
Python中一切皆对象,函数也不例外,而且Python中的函数还是一等对象。函数可以理解为一种可调用对象语法糖。
可调用对象的元接口是call。如果一个类定义了 call 方法,那么它的实例可以作为函数调用。示例如下。
"""
]> pickcard = Cards(range(52))
]> pickcard()
51
]> pickcard()
50
]> callable(pickcard)
True
"""
class Cards:
def init(self, items):
self._items = list(items)
def call(self):
return self._items.pop()
序列
Python 的序列数据模型的元接口很多,但是对象只需要实现 len 和 getitem 两个方法,就能用在绝大部分期待序列的地方,如迭代,【】运算符、切片、for i in 等操作。示例如下:
"""
]> poker = Poker()
支持len运算
]> len(poker)
52
支持【】运算
]> poker【0】
Card(rank='2', suit='spades')
]> poker【-1】
Card(rank='A', suit='hearts')
支持切片运算
]> poker【12::13】
【Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')】
支持 for i in 运算
]> for card in poker: print(card) # doctest: +ELLIPSIS
...
Card(rank='2', suit='spades')
Card(rank='3', suit='spades')
Card(rank='4', suit='spades')
...
支持 in 运算
]> Card('7', 'hearts') in poker
True
"""
import collections
Card = collections.namedtuple('Card', 【'rank', 'suit'】)
class Poker:
ranks = 【str(n) for n in range(2, 11)】 + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def init(self):
self._cards = 【Card(rank, suit) for suit in self.suits
for rank in self.ranks】
def len(self):
return len(self._cards)
def getitem(self, position):
return self._cards【position】
从测试用例上可以看出它具有序列所有特性,即便它是 object 的子类也无妨。因为它的行为像序列,那我们就可以说它是序列。
迭代
Python中,可迭代对象的元接口是iter。迭代器可以从可迭代的对象中获取,iter和next是它的2个主要的元接口。iter 方法使对象支持迭代,next 方法返回序列中的下一个元素。如果没有元素了,那么抛出 StopIteration 异常。
迭代器可以迭代,但是可迭代的对象不是迭代器,也一定不能是自身的迭代器。也就是说,可迭代的对象必须实现 iter 方法,但不能实现 next 方法。
只要实现iter接口的对象,就是迭代鸭子类型,自然就支持所有的迭代运算。示例如下:
"""
]> s = Sentence('hello world')
]> s
Sentence('hello world')
支持迭代list运算
]> list(s)
【'hello', 'world'】
获取迭代器
]> it = iter(s)
支持迭代器next运算
]> next(it)
'hello'
]> next(it)
'world'
]> next(it)
Traceback (most recent call last):
...
StopIteration
支持迭代for运算
]> for w in s: print(w)
hello
world
"""
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def init(self, text):
self.text = text
def repr(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
//代码效果参考:http://www.jhylw.com.cn/020323445.html
def iter(self):word_iter = RE_WORD.finditer(self.text)
return SentenceIter(word_iter)
class SentenceIter():
def init(self, word_iter):
self.word_iter = word_iter
def next(self):
match = next(self.word_iter)
return match.group()
def iter(self):
return self
上面这个例子中,可迭代对象Sentence通过定义迭代器SentenceIter的方式实现。更Pythonic的做法是通过生成器yield来实现。下面是一个示例,能通过上面的所有测试用例,但代码更加精简。
RE_WORD = re.compile('\w+')
class Sentence:
def init(self, text):
self.text = text
def repr(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def iter(self):
for match in RE_WORD.finditer(self.text):
yield match.group()
上下文管理器
Python的with关键字是上下文管理器语法糖,上下文管理器协议包含 enter 和 exit 两个方法。with 语句开始运行时,会在上下文管理器对象上调用 enter 方法。with 语句运行结束后,会在上下文管理器对象上调用 exit 方法,以此扮演 finally 子句的角色。可以看出,上下文管理器简化了 try/finally 模式。下面是一个示例。
"""
ReversePrint对象的上下文管理,进入with块后,标准输出反序打印,
退出with块后,标准输出恢复正常状态。
]> with ReversePrint() as what:
... print('Hello world!')
!dlrow olleH
]> print('Hello world!')
Hello world!
"""
class ReversePrint:
def enter(self):
import sys
self.original_write = sys.stdout.write
sys.stdout.write = self.reverse_write
return 'JABBERWOCKY'
def reverse_write(self, text):
self.original_write(text【::-1】)
def exit(self, exc_type, exc_value, traceback):
import sys
sys.stdout.write = self.original_write
if exc_type is ZeroDivisionError:
print('Please DO NOT divide by zero!')
return True
作者:wahaha02
出处:
本文为博主原创文章,内容欢迎转载或引用,但请注明出处。