前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用好 Python 标准库!少写几百行。

用好 Python 标准库!少写几百行。

作者头像
初代庄主
发布2023-02-20 10:51:00
5220
发布2023-02-20 10:51:00
举报
文章被收录于专栏:初代庄主

概要

整体上来讲 Python 这门语言还是比较注重开发者体验的,一方面表现在其简洁的语法上,另一方面就是其丰富的标准库模块。Python-3.7 版本为标准库引入了一个新的模块 dataclasses ,用这个新模块可以让我们本就简洁的代码再简短不少。


传统的写法

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

代码语言:javascript
复制
#!/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)

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

代码语言:javascript
复制
def __init__(self, a, b):
    self.a = a
    self.b = b

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


改进 __init__ 样板代码

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

代码语言:javascript
复制
#!/usr/bin/evn python3

from dataclasses import dataclass

@dataclass
class 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 来处理

代码语言:javascript
复制
class Person(object):
    # 省略其它代码

    # 用属性语法
    @property
    def full_name(self):
        """返回实例对象的全名
        Return
        ------
        str
        """
        return self.last_name + self.first_name

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

代码语言:javascript
复制
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 也给我们暴露了钩子。下面看代码

代码语言:javascript
复制
#!/usr/bin/evn python3

from dataclasses import asdict, dataclass


@dataclass
class 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 说是为了减少代码量,我怎么没有看到第二个例子有减少多少代码呀!那原理(心法)的事我们就点到为止了,还是来看一下在实战中它能帮我们少写多少代码。


对象转换为字典

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

代码语言:javascript
复制
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 帮忙我们构造的,那它一定知道对象有哪些属性!就是因为它有这个信息,它实现了一个通用的转字典的逻辑。并且这个非常方便,不用多写一行代码。

代码语言:javascript
复制
#!/usr/bin/evn python3

from dataclasses import asdict, dataclass


@dataclass
class 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 还没有出现之后这些还要一些元编程的知识才能写出来。传统的代码和下面这段差不多。

代码语言:javascript
复制
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="四"  

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

代码语言:javascript
复制
python3 main.py
Traceback (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 元编程”,滚开!

代码语言:javascript
复制
#!/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)

同样可以做到只读。

代码语言:javascript
复制
python3 main.py
Traceback (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 是一个比较大的模块功能多了去了,大家看去看吧。“分享” + “收藏” + “点赞” + “在看” 这些只会让我更新的更快。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-01-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 初代庄主 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档