经验之二

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            
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值