用好 Python 标准库可以少写几百行

概要

整体上来讲 Python 这门语言还是比较注重开发者体验的,一方面表现在其简洁的语法上,另一方面就是其丰富的标准库模块。

Python-3.7 版本为标准库引入了一个新的模块 dataclasses ,用这个新模块可以让我们本就简洁的代码再简短不少。


 

传统的写法

以前代码要一行一行的码,处处要体现工匠精神,这东西马虎不得;一不小心就会出 Bug ,后来“声明式”的编程思想,得以实践我们只要说我想要什么效果就行。 在正式讲“声明式”这个新东西之前,我们还是来看一下传统的代码怎么写。

 

#!/usr/bin/evn python3
class Person(object):    def __init__(self,first_name,last_name,age):        """        Parameters        ----------        first_name: str            名字        last_name: str            姓氏        age: int            年龄        """        self.first_name = first_name        self.last_name = last_name        self.age = age
    def full_name(self):        """返回实例对象的全名        Return        ------        str        """        return self.last_name + self.first_name
    def __str__(self):        return f"{self.__class__.__name__}(first_name='{self.first_name}',last_name='{self.last_name}',age={self.age})"
if __name__ == "__main__":    zhang_san = Person("三","张",18)    print(zhang_san.full_name())    print(zhang_san)

这个可以说得上是一五一十,一丝不苟了。问题不大就是比较费人力,不知道我们在多少类中重复如下样板代码。

def __init__(self, a, b):    self.a = a    self.b = b

dataclasses 这个新模块可以把我们从这种重得的劳动中解放出来。


 

改进 __init__ 样板代码

如果 __init__ 只是为了简单的给对象属性赋值,那 dataclasses 会是我们最好的选择;我们只要声明一下就行了,下面直接看代码。

#!/usr/bin/evn python3
from dataclasses import dataclass
@dataclassclass Person(object):    first_name: str    last_name: str    age: int
    def full_name(self):        """返回实例对象的全名        Return        ------        str        """        return self.last_name + self.first_name
if __name__ == "__main__":    zhang_san = Person("三","张",18)    print(zhang_san.full_name())    print(zhang_san)

代码在以肉眼可见的方式在变短,而我们要做的就是声明一下属性名和对应的类型就行;实践中我还发现声明式的另一个好处,就是可以防止一些拼写错误。


 

尝试介入对象的构造过程

前面我们的代码都是为了说明, dataclasses 是一个声明式的模块,我们只要声明自己想要做么就行了;有了招式少了心法。心法就是用来回答 “那 dataclasses 是怎么做到的?” 。

答案是它通过元编程深入的介入对象的构造过程,帮我们实现了一些通用的代码,比如上文说的 __init__ 方法。

另外它还给我们暴露出了一些生命周期钩子,让使用它的人不用了解 Python 元编程也能玩的飞起。现在以我们来解决一下之前留下的一个坑 full_name 函数,这个 full_name 看起来是一个名词但它实际上是一个函数,真的是让人迷惑呢!

 

传统解法 1 @property 来处理​​​​​​​

class Person(object):    # 省略其它代码
    # 用属性语法    @property    def full_name(self):        """返回实例对象的全名        Return        ------        str        """        return self.last_name + self.first_name

 

传统解法 2 直接保存成一个新的属性​​​​​​​

class Person(object):    """    """    def __init__(self,first_name,last_name,age):        """        Parameters        ----------        first_name: str            名字        last_name: str            姓氏        age: int            年龄        """
        self.first_name = first_name        self.last_name = last_name        self.age = age        # 对于这种相当简单的逻辑我们直接写在 __init__ 钩子里也没有问题        # 直接搞成属性        self.full_name = self.last_name + self.first_name

 

好!终于写到这里了。上面的代码实际上就是在用已有的 first_name & last_name 再创建新属性;还记得吗? 

dataclasses 介入了对象的构造过程,对于这种后置的操作 dataclasses 也给我们暴露了钩子。下面看代码​​​​​​​

#!/usr/bin/evn python3
from dataclasses import asdict, dataclass

@dataclassclass Person(object):    first_name: str    last_name: str    age: int
    def __post_init__(self):        self.full_name = self.last_name + self.first_name
if __name__ == "__main__":    zhang_san = Person("三","张",18)    print(zhang_san.full_name)

好的,写到了这里我们基本上讲清理了 dataclasses 的“招式”和“心法”。理解了心法就可以越用越野了。

前面说 dataclasses 说是为了减少代码量,我怎么没有看到第二个例子有减少多少代码呀!那原理(心法)的事我们就点到为止了,还是来看一下在实战中它能帮我们少写多少代码。


 

对象转换为字典

以前总有哪么一些场景要把对象转换成字典,这些重复的代码真的写的让人想吐。​​​​​​​

class Person(object):    """    """    #    # 省略其它代码     #
    def asdict(self):        """返回对象的字典形式        Return        ------        dict        """        return {            'first_name':self.first_name,            'last_name': self.last_name,            'age': self.age        }
if __name__ == "__main__":    zhang_san = Person("三","张",18)    print(zhang_san.asdict())

现在好了,对象是 dataclasses 帮忙我们构造的,那它一定知道对象有哪些属性!就是因为它有这个信息,它实现了一个通用的转字典的逻辑。并且这个非常方便,不用多写一行代码。​​​​​​​

#!/usr/bin/evn python3
from dataclasses import asdict, dataclass

@dataclassclass Person(object):    first_name: str    last_name: str    age: int
if __name__ == "__main__":    zhang_san = Person("三","张",18)    print(asdict(zhang_san))

如果想节约点内存,想把对象转换为 tuple 也是一样的简单,它提供了一个 astuple 方法。


 

安全

以前大家在评价一个 C++ 工程师能力怎么样的时候,总结出了一条比较简单的标准,“看他 const 用的怎么样”。C++ 的 const 讲法可多了去了,有安全、性能、API 是否优雅 ...。今天我们就谈一下 dataclasses 在安全上的应用场景。

假如我们要实现一个功能,“对象一旦创建完成之后就不能更新它的属性,也就是说这个对象是只读的。” 在 dataclasses 还没有出现之后这些还要一些元编程的知识才能写出来。传统的代码和下面这段差不多。​​​​​​

class Person(object):    """    """    def __init__(self,first_name,last_name,age):        """        Parameters        ----------        first_name: str            名字        last_name: str            姓氏        age: int            年龄        """
        self.first_name = first_name        self.last_name = last_name        self.age = age        # 第一步我们要在初始化完成之后打上标记        self._is_inited = True
    # 第二步 检查对于非初始化赋值的情况,我们要报错。    def __setattr__(self,attr,value):        if '_is_inited' in self.__dict__:            raise RuntimeError("read only object .")        else:            object.__setattr__(self,attr,value)
if __name__ == "__main__":    zhang_san = Person("三","张",18)    zhang_san.first_name="四"  

由于运行的时候改了对象的属性,所以它会报错,详细的报错如下。​​​​​​​

python3 main.pyTraceback (most recent call last):  File "/private/tmp/pys/main.py", line 82, in <module>    zhang_san.first_name="四"  File "/private/tmp/pys/main.py", line 72, in __setattr__    raise RuntimeError("read only object .")RuntimeError: read only object .

 

如果使用的是 dataclasses 来构造的对象就完全不用写这么多代码,还什么“Python 元编程”,滚开!​​​​​​​

#!/usr/bin/evn python3
@dataclass(frozen=True)class Person(object):    first_name: str    last_name: str    age: int
if __name__ == "__main__":    zhang_san = Person("三","张",18)    zhang_san.first_name="四"    print(zhang_san)

 

同样可以做到只读。​​​​​​​

python3 main.pyTraceback (most recent call last):  File "/private/tmp/pys/main.py", line 78, in <module>    zhang_san.first_name="四"  File "<string>", line 4, in __setattr__dataclasses.FrozenInstanceError: cannot assign to field 'first_name'

 

结尾

时代在进步,就算是编程这种与机器打交道的事,也是越来越以人为本了。以前我总是说 “用 Python 语言,import 完了之后,差不多就完成了 50% ” 现在看来已经完成了 60% 。

dataclasses 是一个比较大的模块功能多了去了,大家看去看吧。

欢迎“分享” + “收藏” + “点赞” ❤❤

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rocky006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值