1.类方法使用实例
class My(object):
def __init__(self):
self.__pri = 30
@classmethod
def get_pro(cls, instance):
return instance.__pri
obj = My()
print(My.get_pro(obj)) 类直接调用
30 代码简单不做解释
私有属性别名访问法: 需指定包含此属性的类名才可获得
collections.abc 中的 Sequence 实现了 index 和 count 方法
需要则直接继承
可能更喜欢自己实现, 下面列举了自己实现的 index 和 count 方法
class Test(object):
def __init__(self, number):
self.number = number
def index(self, value):
for index, i in enumerate(self.number):
if i == value:
return index
return 'value not exist'
def count(self, value):
d = {}
d[value] = 0
if value not in self.number:
return 0
for i in self.number:
if i == value:
d[value] += 1
return d[value]
obj = Test([1, 2, 3, 4, 5])
print(obj.index(1))
print(obj.index(10))
print(obj.count(7))
print(obj.count(2))
0
value not exist
0
1
2.防止父类属性被修改
@property 可以为现有的实例属性添加新的功能
之前使用 setattr 方法实现禁止实例化对象属性值修改
现在使用 @property 和 @属性名.setter实现
class Father(object):
def __init__(self, number):
self._values = number
class Child(Father):
def __init__(self, number):
super().__init__(number)
@property
def values(self):
return self._values
@values.setter
def values(self, number):
if hasattr(self, '_values'):
raise AttributeError('Can not set attribute')
self._values = number
obj = Child(100) 这里注意名称对应
obj.values = 10 赋值操作会调用@values.setter
print(obj.values)
AttributeError: Can not set attribute
3.原生 setattr 方法的工作方式
class Test(object):
def __init__(self):
self.grade = 0
def __getattr__(self, name):
value = 'hello'
setattr(self, name, value)
return value
obj = Test() 当找不到实例化对象的属性时就会调用__getattr__方法
print(obj.data) 原生 setattr 实现属性赋值
print(obj.grade)
print(obj.__dict__) 显示对象属性的 key, value值
hello
0 属性赋值后则不会再次调用 __getattr__方法
{'grade': 0, 'data': 'hello'}
4.其他描述符方法
上述 __getattr__ 方法只调用了一次,若实现的是 __getattrbute__ 方法, 那么每次在对象上调用 hasattr 或 getattr 函数,此方法都会执行
hasattr 函数可以判断对象是否有某属性,此函数会先在实例字典中查找,再调用 __getattr__ 方法
访问属性时,必然会调用 __getattrbute__ 方法
若此方法会返回其他属性值,那么就会造成无限循环
解决办法是使用 super继承 (setattr方法设置属性值时也是如此)
代码如下
class Test(object):
def __init__(self, data):
self._data = data
def __getattribute__(self, name):
# return self._data[name] 会无限循环递归
data_dict = super().__getattribute__('_data')
return data_dict[name]
obj = Test({'like': 'you'})
print(obj.like)
you
5.用copyreg实现可靠的pickle操作
定义一个游戏类,有等级、生命等属性
import pickle
class Game(object):
def __init__(self):
self.level = 0
self.lives = 3
state = Game()
state.lives -= 1
with open('test.txt', 'wb') as f:
pickle.dump(state, f) 二进制存储并序列化
with open('test.txt', 'rb') as f:
res = pickle.load(f) 反序列化,显示其实例字典
print(res.__dict__)
{'level': 0, 'lives': 2}
但是,游戏都会更新,假如我们现在需要添加一个 points 分数属性
直接在类中添加后,读取旧的保存文档会发现没有成功加入
class Game(object):
def __init__(self, level=0, lives=3, points=5):
self.level = level
self.lives = lives
self.points = points
with open('test.txt', 'rb') as f: 直接读取,并未重新存档
res = pickle.load(f)
print(res.__dict__)
{'level': 0, 'lives': 2} 新属性未添加
解决办法–定义一个辅助函数
class Game(object):
def __init__(self, level=0, lives=3, points=5):
self.level = level
self.lives = lives
self.points = points
def pick_game(game_state):
kwargs = game_state.__dict__
return unpick_game, (kwargs,) 元组存储
def unpick_game(kwargs): 辅助函数
return Game(**kwargs)
copyreg.pickle(Game, pick_game) 绑定
with open('test.txt', 'rb') as f:
res = pickle.load(f)
print(res.__dict__)
{'level': 0, 'lives': 2, 'points': 5}
原因: 辅助函数会直接调用 Game 构造器, 使得反序列化后的实例其属性完备;
若有参数缺失,对应属性则会获得相应默认值
6.一些捷径
First
堆队列
这些元素会按照优先级从高到低的顺序弹出(数值小则优先级高)
import heapq
a = []
heapq.heappush(a, 3)
heapq.heappush(a, 7)
heapq.heappush(a, 5)
print(heapq.heappop(a), heapq.heappop(a), heapq.heappop(a))
3 5 7
Second
bisect 模块采用二分搜索算法,其复杂度是对数级别
而 index 方法是线性级别,此算法会快很多
7.在重视精度的场合,使用 decimal
此类默认提供28个小数位
还有一个内置函数,使开发者自定义精度及舍入方式
假设一个用户打电话耗时3分10秒,每分钟1.3元,每次通话至少0.01元
from decimal import Decimal
rate = Decimal('1.30')
seconds = Decimal(3 * 60 + 10)
cost = seconds * rate / Decimal('60')
print(cost)
4.116666666666666666666666667
现在指定精度为0.01,话费不足0.01按0.01算
from decimal import Decimal, ROUND_UP
rate = Decimal('0.5')
seconds = Decimal('1')
cost = seconds * rate / Decimal('60')
round = cost.quantize(Decimal('0.01'), rounding=ROUND_UP)
print(round)
不指定时结果小于0.01会被忽略
0.01 指定精度0.01