python 的 NamedTuple return 结构是少数几个应该使用可变默认值的地方之一吗?
Are python's NamedTuple return structures one of the few places where mutable defaults should be used?
从 python 函数到 return 结构的方法已在各种帖子中进行了详细讨论。两个不错的 here and here.
但是,除非我错过了它,none 提议的解决方案在设置其成员的同一位置定义结构,而是在分配时重复成员列表(不是 DRY)或依赖位置(容易出错)。
我正在寻找一种 DRY 方法来执行此操作,既可以提高写入速度,又可以避免重复自己时常见的参数未对齐错误。
下面的代码片段显示了执行此操作的三种尝试。为了简洁起见,示例的结构只包含一个元素,但意图显然是结构包含多个元素。
这三个方法是 DRY,将结构定义嵌入到 returned 实例的初始化中。
方法 1 强调了对更好方法的需求,但说明了 DRY 寻求的语法,其中结构及其填充方式(在 运行 时间决定)位于同一位置,即 dict()
呼唤。
方法 2 使用 typing.NamedTuple
并且似乎有效。但是它使用可变默认值来做到这一点
方法 3 遵循方法 2 的方法,使用 dataclasses.dataclass
而不是 typing.NamedTuple
。它失败了,因为前者明确禁止可变默认值,引发 ValueError: mutable default is not allowed
from collections import namedtuple
from dataclasses import dataclass
from typing import NamedTuple, List, Tuple
# Method 1
def ret_dict(foo_: float, bar_: float) -> Tuple:
return_ = dict(foo_bar=[foo_, bar_])
_ = namedtuple('_', return_.keys())
return _(*return_.values())
# Method 2
def ret_nt(foo_: float, bar_: float) -> 'ReturnType':
class ReturnType(NamedTuple):
foo_bar: List[float] = [foo_, bar_] # Mutable default value allowed
return ReturnType()
# Method 3
def ret_dc(foo_: float, bar_: float) -> 'ReturnType':
@dataclass
class ReturnType:
foo_bar: List[float] = [foo_, bar_] # raises ValueError: mutable default is not allowed
return ReturnType()
def main():
rt1 = ret_dict(1, 0)
rt1.foo_bar.append(3)
rt2 = ret_dict(2, 0)
print(rt1)
print(rt2)
rt1 = ret_nt(1, 0)
rt1.foo_bar.append(3) # amending the mutable default does not affect subsequent calls
rt2 = ret_nt(2, 0)
print(rt1)
print(rt2)
rt1 = ret_dc(1, 0)
rt1.foo_bar.append(3) # amending the default does not affect subsequent calls
rt2 = ret_dc(2, 0)
print(rt1)
print(rt2)
if __name__ == "__main__":
main()
出现以下问题:
方法 2 是明智的 pythonic 方法吗?
一个问题是可变默认值有点禁忌,尤其是对于函数参数。然而,我想知道它们在这里的使用是否合适,因为附加的代码表明这些 NamedTuple
默认值(可能还有整个 ReturnType
定义)在每次函数调用时都会被评估,这与它的函数参数默认值相反在我看来只评估一次并永远存在(因此出现问题)。
另一个问题是 dataclasses 模块似乎已经特意明确禁止这种用法。在这种情况下,这个决定是否过于教条?或者是否有理由反对方法 2?
这样效率低吗?
如果方法 2 的语法意味着:
,我会很高兴
1 - 仅在第一遍定义 ReturnType
一次
2 - 在每次传递时使用给定的(动态设置的)初始化调用 __init__()
不过,恐怕它的意思可能是:
1 - 定义 ReturnType
及其每次传递的默认值
2 - 在每次传递时使用给定的(动态设置的)初始化调用 __init__()
当调用处于“紧密”循环中时,是否应该担心在每次传递时重新定义矮胖的 ReturnType
s 的效率低下?每当在函数内部定义 class 时,是否会出现这种低效率?应该在函数内部定义 classes 吗?
是否有(希望是好的)方法来使用新的 dataclasses
模块 (python 3.7) 实现 DRY 定义实例化?
最后,有没有更好的DRY定义-实例化语法?
However, I am afraid that it may instead mean the following:
1 - Define ReturnType
and its defaults on every pass
2 - call __init__()
with the given (dynamically set) initialization on every pass
就是这个意思,又要花很多时间和space。此外,它会使您的注释无效 - -> 'ReturnType'
需要模块级别的 ReturnType
定义。它还会破坏酸洗。
坚持使用模块级别 ReturnType
,不要使用可变默认值。或者,如果您想要的只是通过点符号访问成员,而您并不真正关心制作有意义的类型,只需使用 types.SimpleNamespace
:
return types.SimpleNamespace(thing=whatever, other_thing=stuff)
从 python 函数到 return 结构的方法已在各种帖子中进行了详细讨论。两个不错的 here and here.
但是,除非我错过了它,none 提议的解决方案在设置其成员的同一位置定义结构,而是在分配时重复成员列表(不是 DRY)或依赖位置(容易出错)。
我正在寻找一种 DRY 方法来执行此操作,既可以提高写入速度,又可以避免重复自己时常见的参数未对齐错误。
下面的代码片段显示了执行此操作的三种尝试。为了简洁起见,示例的结构只包含一个元素,但意图显然是结构包含多个元素。
这三个方法是 DRY,将结构定义嵌入到 returned 实例的初始化中。
方法 1 强调了对更好方法的需求,但说明了 DRY 寻求的语法,其中结构及其填充方式(在 运行 时间决定)位于同一位置,即 dict()
呼唤。
方法 2 使用 typing.NamedTuple
并且似乎有效。但是它使用可变默认值来做到这一点
方法 3 遵循方法 2 的方法,使用 dataclasses.dataclass
而不是 typing.NamedTuple
。它失败了,因为前者明确禁止可变默认值,引发 ValueError: mutable default is not allowed
from collections import namedtuple
from dataclasses import dataclass
from typing import NamedTuple, List, Tuple
# Method 1
def ret_dict(foo_: float, bar_: float) -> Tuple:
return_ = dict(foo_bar=[foo_, bar_])
_ = namedtuple('_', return_.keys())
return _(*return_.values())
# Method 2
def ret_nt(foo_: float, bar_: float) -> 'ReturnType':
class ReturnType(NamedTuple):
foo_bar: List[float] = [foo_, bar_] # Mutable default value allowed
return ReturnType()
# Method 3
def ret_dc(foo_: float, bar_: float) -> 'ReturnType':
@dataclass
class ReturnType:
foo_bar: List[float] = [foo_, bar_] # raises ValueError: mutable default is not allowed
return ReturnType()
def main():
rt1 = ret_dict(1, 0)
rt1.foo_bar.append(3)
rt2 = ret_dict(2, 0)
print(rt1)
print(rt2)
rt1 = ret_nt(1, 0)
rt1.foo_bar.append(3) # amending the mutable default does not affect subsequent calls
rt2 = ret_nt(2, 0)
print(rt1)
print(rt2)
rt1 = ret_dc(1, 0)
rt1.foo_bar.append(3) # amending the default does not affect subsequent calls
rt2 = ret_dc(2, 0)
print(rt1)
print(rt2)
if __name__ == "__main__":
main()
出现以下问题:
方法 2 是明智的 pythonic 方法吗?
一个问题是可变默认值有点禁忌,尤其是对于函数参数。然而,我想知道它们在这里的使用是否合适,因为附加的代码表明这些 NamedTuple
默认值(可能还有整个 ReturnType
定义)在每次函数调用时都会被评估,这与它的函数参数默认值相反在我看来只评估一次并永远存在(因此出现问题)。
另一个问题是 dataclasses 模块似乎已经特意明确禁止这种用法。在这种情况下,这个决定是否过于教条?或者是否有理由反对方法 2?
这样效率低吗?
如果方法 2 的语法意味着:
,我会很高兴1 - 仅在第一遍定义 ReturnType
一次
2 - 在每次传递时使用给定的(动态设置的)初始化调用 __init__()
不过,恐怕它的意思可能是:
1 - 定义 ReturnType
及其每次传递的默认值
2 - 在每次传递时使用给定的(动态设置的)初始化调用 __init__()
当调用处于“紧密”循环中时,是否应该担心在每次传递时重新定义矮胖的 ReturnType
s 的效率低下?每当在函数内部定义 class 时,是否会出现这种低效率?应该在函数内部定义 classes 吗?
是否有(希望是好的)方法来使用新的 dataclasses
模块 (python 3.7) 实现 DRY 定义实例化?
最后,有没有更好的DRY定义-实例化语法?
However, I am afraid that it may instead mean the following:
1 - Define
ReturnType
and its defaults on every pass2 - call
__init__()
with the given (dynamically set) initialization on every pass
就是这个意思,又要花很多时间和space。此外,它会使您的注释无效 - -> 'ReturnType'
需要模块级别的 ReturnType
定义。它还会破坏酸洗。
坚持使用模块级别 ReturnType
,不要使用可变默认值。或者,如果您想要的只是通过点符号访问成员,而您并不真正关心制作有意义的类型,只需使用 types.SimpleNamespace
:
return types.SimpleNamespace(thing=whatever, other_thing=stuff)