Python 如何设计面向对象的类(下)

本文将在上篇文章二维向量 Vector2d 类的基础上,定义表示多维向量的 Vector 类。

Python 如何设计面向对象的类(上)

Python 是一门高级语言,支持面向对象设计,如何设计一个符合 Python 风格的面向对象的类,是一个比较复杂的问题,本文提供一个参考,表达一种思路,探究一层原理。

Python 变量小秘密

本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 变量全都是引用 跟其他编程语言不同,Python 的变量不是盒子,不会存储数据,它们只是引用,就像标签一样,贴在对象上面。 比如: >>> a = [1, 2, 3] >>> b = a >>> a.append(4) >>> b [1, 2, 3, 4] >>> b is a True a 变量和 b 变量引用的是同一个列表[1, 2, 3]。b 可以叫做 a 的别名。 比较来看: >>> a = [1, 2, 3] >>> c = [1, 2, 3] >>> c == a True >>> c is a False c 引用的是另外一个列表,虽然和 a 引用的列表的值相等,但是它们是不同的对象。 浅复制与深复制 浅复制是指只复制最外层容器,副本中的元素是源容器中元素的引用。如果所有元素都是不可变的,那么这样没有问题,还能节省内容。但是,如果有可变的元素,那么结果可能会出乎意料之外。构造方法或[:]做的都是浅复制。 示例: >>> x1 = [3, [66, 55, 44], (7, 8, 9)] ## x2是x1的浅复制 >>> x2 = list(x1) ## 不可变元素没有影响 >>> x1.append(100) >>> x1 [3, [66, 55, 44], (7, 8, 9), 100] >>> x2 [3, [66, 55, 44], (7, 8, 9)] ## x1[1]是列表,可变元素会影响x2 ## 因为它们引用的是同一个对象 >>> x1[1].remove(55) >>> x1 [3, [66, 44], (7, 8, 9), 100] >>> x2 [3, [66, 44], (7, 8, 9)] ## x2[1]也会反过来影响x1 >>> x2[1] += [33, 22] >>> x1 [3, [66, 44, 33, 22], (7, 8, 9), 100] >>> x2 [3, [66, 44, 33, 22], (7, 8, 9)] ## 不可变元组也不会有影响 ## +=运算符创建了一个新元组 >>> x2[2] += (10, 11) >>> x1 [3, [66, 44, 33, 22], (7, 8, 9), 100] >>> x2 [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)] 深复制是指我们常规理解的复制,副本不共享内部对象的引用,是完全独立的一个副本。这可以借助 copy.deepcopy 来实现。 ...

Python 编解码问题与文本文件处理

本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 编解码器 在字符与字节之间的转换过程称为编解码,Python 自带了超过 100 种编解码器,比如: ascii(英文体系) gb2312(中文体系) utf-8(全球通用) latin1 utf-16 编解码器一般有多个别名,比如utf8、utf-8、U8。 这些编解码器可以传给 open()、str.encode()、bytes.decode()等函数的 encoding 参数。 UnicodeEncodeError 多数非 UTF 编解码器(比如 cp437)只能处理 Unicode 字符的一小部分子集。把字符转换成字节时,如果目标编码中没有定义这个字符,那么就会抛出 UnicodeEncodeError 异常。 处理方式一:使用 utf8 编码。 处理方式二:添加 errors 参数: ## 忽略 如b'So Paulo' city.encode("cp437", errors="ignore") ## 替换为? 如b'S?o Paulo' city.encode("cp437", errors="replace") ## 替换为XML实体 如b'São Paulo' city.encode("cp437", errors="xmlcharrefreplace") UnicodeDecodeError 把字节转换为字符时,遇到无法转换的字节时会抛出 UnicodeDecodeError 异常。这是因为不是每个字节都包含有效的 ASCII 字符,也不是每个字符都是有效的 UTF-8。 处理方式也有两种,跟上面一样。 SyntaxError Python3 默认使用 UTF-8 编码源码。如果加载的.py 模块中包含 UTF-8 之外的数据,而且没有声明编码,就会抛出 SyntaxError 异常。 处理方式是在文件顶部添加 coding 注释: ...

Python 字符与字节新编

本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 字符 字符是一个信息单位,简单来讲就是一个字母、数字、标点符号、汉字等。 字符的最佳定义是 Unicode 字符: 它是一个全球化的标准,能表示世界上所有语言的字符。Unicode 字符的标识(码位)是以 4~6 个十六进制数字表示的,并且加前缀U+。 字节 字节是计算机信息计量单位,一个字节代表八个比特,存储的数值范围为 0~255。 字节跳动(ByteDance)互联网公司的字节就是这个字节。 字节是机器的,字符是人类的。 把人类字符转换为机器字节时使用的算法叫做编码,反之叫做解码。 算法不同,字节与字符的关系也不同: bytes 和 bytearray 字节实际上是个二进制序列。不可变 bytes 类型和可变 bytearray 类型是用来存储二进制序列的,它们的示例如下: >>> cafe = bytes("café", encoding="utf_8") >>> cafe b'caf\xc3\xa9' >>> cafe[0] 99 >>> cafe[:1] b'c' >>> cafe_arr = bytearray(cafe) >>> cafe_arr bytearray(b'caf\xc3\xa9') >>> cafe_arr[-1:] bytearray(b'\xa9') 特别的是 cafe[0]返回了整数,cafe[:1]返回了二进制序列,这是因为s[0] == s[:1]只对 str 类型成立,而对于其他类型来说,s[i]返回一个元素,s[i:i+1]返回一个相同类型的序列。 二进制序列实际上是整数序列。它们的字面量表示法包含 ASCII 字符(ASCII 只能表示英文体系的字符),比如 cafe 的b'caf\xc3\xa9',具体规则是: 从空格到~的字符直接使用 ASCII 字符 制表符\t、换行符\n、回车符\r、转义符\\ 其他字符用十六进制转义序列,比如\x00空字节 构建 bytes 和 bytearray 对象的方式有以下几种: 一个 str 对象和一个 encoding 关键字参数 一个可迭代对象,数值在 0~255 一个实现了缓冲协议的对象,如 bytes、bytearray、memoryview、array.array memoryview 和 struct memoryview 允许在二进制数据结构之间共享内存,struct 能从序列中提取结构化信息。 ...

Python 函数装饰器高级用法

在了解了 Python 函数装饰器基础知识和闭包之后,开始正式学习函数装饰器。

关于 Python 闭包的一切

任何把函数当做一等对象的语言,它的设计者都要面对一个问题:作为一等对象的函数在某个作用域中定义,但是可能会在其他作用域中调用,如何处理自由变量?

fastjson 知多少

本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 fastjson 介绍 fastjson 是一个 Java 库,用来在 Java 对象和 JSON 之间相互转换。 fastjson 的目标(官方原话): 我翻译一下: 性能非常好; 封装非常高; 转换非常强; 泛型也支持; 自定义表示; 复杂也支持; 这 6 个目标,书读少的我理解起来费劲,看着这些字,视线开始模糊…突然这些字动了,不断变幻,凝聚成了两个字:“牛逼”。阿里巴巴也确实做到了,fastjson(24.9k star)作为国产原创库,PK 掉了 jackson(7.6k star)和 gson(21.3k star),在国内应用领域坐上了头把交椅。 安装 fastsjon Maven 引入: <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>VERSION_CODE</version> </dependency> 版本可以在 https://mvnrepository.com/artifact/com.alibaba/fastjson 查找: 虽然 2.0.x 版本出来了,但是使用得多的还是 1.2.x 版本。至少得 1.2.48 以上,因为这以下的版本之前出过轰动全国的安全漏洞。 入门示例 Group.java public class Group { private Long id; private String name; private List<User> users = new ArrayList<User>(); public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; } public void addUser(User user) { users.add(user); } } User.java ...

Python 函数装饰器基础知识

函数装饰器是 Python 语言最优秀的设计之一,它以非常简洁的方式增强了函数的行为,让崎岖不平之路变得平坦顺畅。

Python 设计模式知多少

本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 设计模式 设计模式是前辈们经过相当长的一段时间的试验和错误总结出来的最佳实践。我找到的资料列举了以下这些设计模式:工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式、适配器模式、桥接模式、过滤器模式、组合模式、装饰器模式、外观模式、享元模式、代理模式、责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、空对象模式、策略模式、模板模式、访问者模式、MVC 模式、业务代表模式、组合实体模式、数据访问对象模式、前端控制器模式、拦截过滤器模式、服务定位器模式、传输对象模式,共 33 种。 这些设计模式在纯面向对象编程语言中使用最多。**Python 拥有一等函数,既不需要使用某些设计模式,也减少了某些设计模式样板代码。**本文将使用一等函数实现策略模式和命令模式,研究 Python 代码是如何简化的。 策略模式 策略模式概述:“定义一系列算法,把它们一一封装起来,并且使它们可以相互替换。本模式使得算法可以独立于使用它的客户而变化。” 经典实现 示例,根据客户的属性或订单中的商品计算折扣,规则如下: 有 1000 或以上积分的客户,每个订单享 5%折扣。 同一订单中,单个商品的数量达到 20 个或以上,享 10%折扣。 订单中的不同商品达到 10 个或以上,享 7%折扣。 这很适合用策略模式来做,UML 类图设计如下: 上下文,集成算法的类,图中 Order 会根据不同的算法计算折扣。 策略,实现不同算法的组件的共同接口,图中 Promotion 是个抽象类。 具体策略,策略的具体子类,图中 FidelityPromo、BulkItemPromo、LargeOrderPromo 分别对应上面 3 条计算折扣规则。 代码实现如下: from abc import ABC, abstractmethod from collections import namedtuple Customer = namedtuple('Customer', 'name fidelity') class LineItem: def __init__(self, product, quantity, price): self.product = product self.quantity = quantity self.price = price def total(self): return self.price * self.quantity class Order: # the Context def __init__(self, customer, cart, promotion=None): self.customer = customer self.cart = list(cart) self.promotion = promotion def total(self): if not hasattr(self, '__total'): self.__total = sum(item.total() for item in self.cart) return self.__total def due(self): if self.promotion is None: discount = 0 else: discount = self.promotion.discount(self) return self.total() - discount def __repr__(self): fmt = '<Order total: {:.2f} due: {:.2f}>' return fmt.format(self.total(), self.due()) class Promotion(ABC): # the Strategy: an Abstract Base Class @abstractmethod def discount(self, order): """Return discount as a positive dollar amount""" class FidelityPromo(Promotion): # first Concrete Strategy """5% discount for customers with 1000 or more fidelity points""" def discount(self, order): return order.total() * .05 if order.customer.fidelity >= 1000 else 0 class BulkItemPromo(Promotion): # second Concrete Strategy """10% discount for each LineItem with 20 or more units""" def discount(self, order): discount = 0 for item in order.cart: if item.quantity >= 20: discount += item.total() * .1 return discount class LargeOrderPromo(Promotion): # third Concrete Strategy """7% discount for orders with 10 or more distinct items""" def discount(self, order): distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: return order.total() * .07 return 0 实现策略模式的关键代码是 Promotion 类,它是一个抽象基类,通过继承 abc.ABC 来定义。 ...