老程序员分享:Python数据模型及Pythonic编程

简介: 老程序员分享:Python数据模型及Pythonic编程

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 的序列数据模型的元接口很多,但是对象只需要实现 lengetitem 两个方法,就能用在绝大部分期待序列的地方,如迭代,【】运算符、切片、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。迭代器可以从可迭代的对象中获取,iternext是它的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关键字是上下文管理器语法糖,上下文管理器协议包含 enterexit 两个方法。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


出处:


本文为博主原创文章,内容欢迎转载或引用,但请注明出处。

相关文章
|
1月前
|
SQL 自然语言处理 数据库
【Azure Developer】分享两段Python代码处理表格(CSV格式)数据 : 根据每列的内容生成SQL语句
本文介绍了使用Python Pandas处理数据收集任务中格式不统一的问题。针对两种情况:服务名对应多人拥有状态(1/0表示),以及服务名与人名重复列的情况,分别采用双层for循环和字典数据结构实现数据转换,最终生成Name对应的Services列表(逗号分隔)。此方法高效解决大量数据的人工处理难题,减少错误并提升效率。文中附带代码示例及执行结果截图,便于理解和实践。
|
24天前
|
XML JSON API
淘宝商品详情API的调用流程(python请求示例以及json数据示例返回参考)
JSON数据示例:需要提供一个结构化的示例,展示商品详情可能包含的字段,如商品标题、价格、库存、描述、图片链接、卖家信息等。考虑到稳定性,示例应基于淘宝开放平台的标准响应格式。
|
24天前
|
人工智能 Java 数据安全/隐私保护
[oeasy]python081_ai编程最佳实践_ai辅助编程_提出要求_解决问题
本文介绍了如何利用AI辅助编程解决实际问题,以猫屎咖啡的购买为例,逐步实现将购买斤数换算成人民币金额的功能。文章强调了与AI协作时的三个要点:1) 去除无关信息,聚焦目标;2) 将复杂任务拆解为小步骤,逐步完成;3) 巩固已有成果后再推进。最终代码实现了输入验证、单位转换和价格计算,并保留两位小数。总结指出,在AI时代,人类负责明确目标、拆分任务和确认结果,AI则负责生成代码、解释含义和提供优化建议,编程不会被取代,而是会更广泛地融入各领域。
83 28
|
3天前
|
数据采集 存储 缓存
Python爬虫与代理IP:高效抓取数据的实战指南
在数据驱动的时代,网络爬虫是获取信息的重要工具。本文详解如何用Python结合代理IP抓取数据:从基础概念(爬虫原理与代理作用)到环境搭建(核心库与代理选择),再到实战步骤(单线程、多线程及Scrapy框架应用)。同时探讨反爬策略、数据处理与存储,并强调伦理与法律边界。最后分享性能优化技巧,助您高效抓取公开数据,实现技术与伦理的平衡。
20 4
|
18天前
|
存储 数据库 Python
利用Python获取网络数据的技巧
抓起你的Python魔杖,我们一起进入了网络之海,捕捉那些悠游在网络中的数据鱼,想一想不同的网络资源,是不是都像数不尽的海洋生物,我们要做的,就是像一个优秀的渔民一样,找到他们,把它们捕获,然后用他们制作出种种美味。 **1. 打开魔法之门:请求包** 要抓鱼,首先需要一个鱼网。在Python的世界里,我们就是通过所谓的“请求包”来发送“抓鱼”的请求。requests是Python中常用的发送HTTP请求的库,用它可以方便地与网络上的资源进行交互。所谓的GET,POST,DELETE,还有PUT,这些听起来像偶像歌曲一样的单词,其实就是我们鱼网的不同方式。 简单用法如下: ``` im
51 14
|
13天前
|
数据采集 搜索推荐 API
Python 原生爬虫教程:京东商品列表页面数据API
京东商品列表API是电商大数据分析的重要工具,支持开发者、商家和研究人员获取京东平台商品数据。通过关键词搜索、分类筛选、价格区间等条件,可返回多维度商品信息(如名称、价格、销量等),适用于市场调研与推荐系统开发。本文介绍其功能并提供Python请求示例。接口采用HTTP GET/POST方式,支持分页、排序等功能,满足多样化数据需求。
|
15天前
|
数据采集 存储 JSON
用Python爬虫抓取数据并保存为JSON的完整指南
用Python爬虫抓取数据并保存为JSON的完整指南
|
30天前
|
人工智能 编解码 算法
如何在Python下实现摄像头|屏幕|AI视觉算法数据的RTMP直播推送
本文详细讲解了在Python环境下使用大牛直播SDK实现RTMP推流的过程。从技术背景到代码实现,涵盖Python生态优势、AI视觉算法应用、RTMP稳定性及跨平台支持等内容。通过丰富功能如音频编码、视频编码、实时预览等,结合实际代码示例,为开发者提供完整指南。同时探讨C接口转换Python时的注意事项,包括数据类型映射、内存管理、回调函数等关键点。最终总结Python在RTMP推流与AI视觉算法结合中的重要性与前景,为行业应用带来便利与革新。
|
29天前
|
Web App开发 数据采集 前端开发
Python + Chrome 爬虫:如何抓取 AJAX 动态加载数据?
Python + Chrome 爬虫:如何抓取 AJAX 动态加载数据?
|
1月前
|
数据库 Python
【YashanDB知识库】python驱动查询gbk字符集崖山数据库CLOB字段,数据被驱动截断
【YashanDB知识库】python驱动查询gbk字符集崖山数据库CLOB字段,数据被驱动截断