Python中有速记初始化器吗?

Posted

技术标签:

【中文标题】Python中有速记初始化器吗?【英文标题】:Is there a shorthand initializer in Python? 【发布时间】:2018-06-25 09:15:53 【问题描述】:

我没有找到可以初始化调用者选择的变量的简短构造函数调用的参考。我在找

class AClass:
    def __init__(self):
        pass

instance = AClass(var1=3, var2=5)

而不是写的更重

class AClass:
    def __init__(self, var1, var2):
        self.var1 = var1
        self.var2 = var2

或者更重的

instance = AClass()
instance.var1 = 3
instance.var2 = 5

我错过了什么吗?

【问题讨论】:

不幸的是,您没有遗漏任何东西。您可能想查看attrs module。 How do I avoid the "self.x = x; self.y = y; self.z = z" pattern in __init__?的可能重复 【参考方案1】:

这是一个很好的问题,对我来说也是一个难题。

在现代 Python 世界中,有 三个(优秀)速记初始化器这个术语很聪明,我正在采用它),取决于你的需要。 __init__ 方法不需要任何步法(这是您首先要避免的)。

命名空间对象

如果您希望为实例分配任意值(即不由类强制执行),则应使用称为 namespace 的特定数据结构。命名空间对象是一个可通过点符号访问的对象,您基本上可以为其分配您想要的内容。

您可以从 argparse 导入 Namespace 类(此处介绍:How do I create a Python namespace (argparse.parse_args value)?)。从 Python 3.3 开始。 SimpleNamespace 类可从标准类型包中获得。

from types import SimpleNamespace
instance = SimpleNamespace(var1=var1, var2=var2)

你也可以写:

instance = SimpleNamespace()
instance.var1 = var1
instance.var2 = var2

假设它是“快速而肮脏的方式”,在许多情况下都可以使用。一般来说,甚至不需要声明你的类。

如果您希望您的实例仍然有一些方法和属性,您仍然可以这样做:

class AClass(Namespace):
    def mymethod(self, ...):
        pass

然后:

instance = AClass(var1=var1, var2=var2, etc.)

这为您提供了最大的灵活性。

命名元组

另一方面,如果您希望类强制这些属性,那么您还有另一个更可靠的选择。

命名元组产生不可变的实例,这些实例被一劳永逸地初始化。将它们视为普通的元组,但每个项目也可以通过点符号访问。此类 namedtuple 是 Python 标准发行版的一部分。这就是你生成类的方式:

from collections import namedtuple
AClass = namedtuple("AClass", "var1 var2")

请注意定义是多么酷和简短,而不是 __init__ 方法需要。之后你就可以真正完成你的课程了。

然后创建一个对象:

instance = AClass(var1, var2)

instance = AClass(var1=var1, var2=var2)

名单

但是,如果您希望该实例是可变的,即允许您更新实例的属性,该怎么办?答案是命名列表(也称为 RecordClass)。从概念上讲,它就像一个普通列表,其中的项目也可以使用点符号访问。

有多种实现方式。我个人使用恰当命名的namedlist。

语法相同:

from namedlist import namedlist
AClass = namedlist("AClass", "var1 var2")

然后创建一个对象:

instance = AClass(var1, var2)

或:

instance = AClass(var1=var1, var2=var2)

然后你可以修改它们:

instance.var1 = var3

但不能添加未定义的属性。

>>> instance.var4 = var4
  File "<stdin>", line 1, in <module>
AttributeError: 'instance' object has no attribute 'var4'

用法

这是我的两位:

    Namespace 对象是为了最大的灵活性,甚至不需要声明一个类;存在行为不正常的实例的风险(但 Python 是一种供成年人使用的语言)。如果您只有一个实例和/或您知道自己在做什么,那将是可行的方法。

    namedtuple 类生成器非常适合为函数返回生成对象(请参阅 Raymond Hettinger 的讲座中的 brief explanation)。与其返回用户需要在文档中查找的平淡无奇的元组,返回的元组是不言自明的(目录或帮助会做到这一点)。而且它无论如何都与元组使用兼容(例如k,v, z = my_func())。加上它是不可变的,这有它自己的优势。

    namedlist 类生成器在很多情况下都很有用,包括当您需要从函数返回多个值,然后需要在稍后阶段修改(并且您仍然可以解压缩他们:k, v, z = instance)。如果您需要来自具有强制属性的适当类的可变对象,那可能是首选解决方案。

如果你用得好,这可能会大大减少编写类和处理实例所花费的时间!


更新(2020 年 9 月)

@PPC:你的梦想成真了。

自 Python 3.7 起,一个新工具可作为标准使用:dataclasses(不出所料,命名列表包的设计者 Eric V. Smith 也是它的幕后推手)。

本质上,它提供了类变量的自动初始化。

from dataclasses import dataclass

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

(来自官方文档)

@dataclass 装饰器会自动添加__init__() 方法:

def __init__(self, name: str, unit_price: float, quantity_on_hand: int=0):
    self.name = name
    self.unit_price = unit_price
    self.quantity_on_hand = quantity_on_hand

恕我直言,这是一个非常漂亮的 Python 解决方案。

Eric 还在 github 上维护了一个 backport of dataclasses,用于 Python 3.6。

【讨论】:

感谢您的更新(您的 *** 和 EV Smith 的代码):)【参考方案2】:

您可以直接更新对象的__dict__ 属性,这是存储属性的位置

class AClass:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

c = AClass(var1=1, var2='a')

【讨论】:

而你付出的代价是你不再对构造函数参数进行错误检查。 @DietrichEpp 您总是可以在update 之前插入一些验证步骤来清理/检查kwargs 这符合 OP 的要求,但在 Python 中可能会被认为是糟糕的风格。 “Python 之禅”python.org/dev/peps/pep-0020 原则之一是“显式优于隐式”,自动分配无名属性不是显式的。【参考方案3】:

您可以使用对象属性的字典表示,并使用为构造函数提供的关键字参数更新其元素:

class AClass:
    def __init__(self, **kwargs):
        self.__dict__.update(**kwargs)

instance = AClass(var1=3, var2=5)
print(instance.var1, instance.var2) # prints 3 5

但是,请考虑this question 及其答案,并考虑其风格。除非您知道自己在做什么,否则最好一一明确设置参数。以后你和其他人会更好地理解它——显式比隐式好。如果您以__dict__.update 的方式进行操作,请正确记录。

【讨论】:

【参考方案4】:

试试

class AClass:
    def __init__(self, **vars):
        self.var1 = vars.get('var1')

【讨论】:

这只会节省在方法定义中写入属性名称。你仍然被困在写一堆作业 self.var1=, self.var2=...

以上是关于Python中有速记初始化器吗?的主要内容,如果未能解决你的问题,请参考以下文章

我可以用 null 初始化一个迭代器吗?如果不是,为啥?

javascript 速记对象初始化器

C语言速记5(字符串)

some_dict.items()是Python中的迭代器吗?

VSCode 中有设备文件资源管理器吗?

ES6对象的扩展