Python:程序化 class 使用 locals() 初始化实例变量
Python: programmatic class instance variable initialisation with locals()
我有一个 class,其中包含许多具有默认值的实例变量,可以选择在实例化中覆盖它们(注意:没有可变的默认参数)。
由于多次写self.x = x
等是多余的,所以我以编程方式初始化变量。
为了说明,请考虑这个示例(为了简洁起见,它只有 5 个实例变量并且省略了任何方法):
示例:
# The "painful" way
class A:
def __init__(self, a, b=2, c=3, d=4.5, e=5):
self.a = a
self.b = b
self.c = c
self.d = d
self.e = e
# The "lazy" way
class B:
def __init__(self, a, b=2, c=3, d=4.5, e=5):
self.__dict__.update({k: v for k, v in locals().items() if k!='self'})
# The "better lazy" way suggested
class C:
def __init__(self, a, b=2, c=3, d=4.5, e=5):
for k, v in locals().items():
if k != 'self':
setattr(self, k, v)
x = A(1, c=7)
y = B(1, c=7)
z = C(1, c=7)
print(x.__dict__) # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}
print(y.__dict__) # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}
print(z.__dict__) # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}
所以为了让我的生活更轻松,我使用了 class B 中所示的成语,它产生与 A 相同的结果。
这是不好的做法吗?有什么陷阱吗?
附录:
使用这个成语的另一个原因是为了节省一些 space - 我打算在 MicroPython. For whatever reason Because locals work differently there 中使用它,只有 class A 中显示的方式在其中起作用。
尝试更像 Python 的方法:
class C:
def __init__(self,a,b=2,c=3,d=4.5,e=5):
for k,v in locals().iteritems():
setattr(self,k,v)
c = C(1)
print c.a, c.b
1 2
这种方法可能会长一两行,但行的长度更短,并且您的意图不那么复杂。此外,任何可能尝试重用您的代码的人都将能够按预期访问您的对象的属性。
希望对您有所帮助。
编辑:删除了使用 kwargs 的第二种方法,因为它没有解决默认变量要求。
此处重要的一点是,如果按照您的示例 class B 显示的那样进行操作,您的代码的用户将无法按预期访问对象的属性。
我实际上建议使用 class A
中显示的代码。您拥有的是重复 代码,而不是冗余代码,重复并不总是 不好。您只需编写 __init__
一次,并且为每个实例变量保留一个赋值是很好的文档(明确且清晰),用于说明您的 class 期望的实例变量。
不过要记住的一件事是 太多 您可以初始化为不同参数的变量可能表明您的 class 需要重新设计。将一些单独的参数分组到单独的列表、dict 甚至额外的 classes 中是否更有意义?
我有一个 class,其中包含许多具有默认值的实例变量,可以选择在实例化中覆盖它们(注意:没有可变的默认参数)。
由于多次写self.x = x
等是多余的,所以我以编程方式初始化变量。
为了说明,请考虑这个示例(为了简洁起见,它只有 5 个实例变量并且省略了任何方法):
示例:
# The "painful" way
class A:
def __init__(self, a, b=2, c=3, d=4.5, e=5):
self.a = a
self.b = b
self.c = c
self.d = d
self.e = e
# The "lazy" way
class B:
def __init__(self, a, b=2, c=3, d=4.5, e=5):
self.__dict__.update({k: v for k, v in locals().items() if k!='self'})
# The "better lazy" way suggested
class C:
def __init__(self, a, b=2, c=3, d=4.5, e=5):
for k, v in locals().items():
if k != 'self':
setattr(self, k, v)
x = A(1, c=7)
y = B(1, c=7)
z = C(1, c=7)
print(x.__dict__) # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}
print(y.__dict__) # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}
print(z.__dict__) # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}
所以为了让我的生活更轻松,我使用了 class B 中所示的成语,它产生与 A 相同的结果。
这是不好的做法吗?有什么陷阱吗?
附录:
使用这个成语的另一个原因是为了节省一些 space - 我打算在 MicroPython. For whatever reason Because locals work differently there 中使用它,只有 class A 中显示的方式在其中起作用。
尝试更像 Python 的方法:
class C:
def __init__(self,a,b=2,c=3,d=4.5,e=5):
for k,v in locals().iteritems():
setattr(self,k,v)
c = C(1)
print c.a, c.b
1 2
这种方法可能会长一两行,但行的长度更短,并且您的意图不那么复杂。此外,任何可能尝试重用您的代码的人都将能够按预期访问您的对象的属性。
希望对您有所帮助。
编辑:删除了使用 kwargs 的第二种方法,因为它没有解决默认变量要求。
此处重要的一点是,如果按照您的示例 class B 显示的那样进行操作,您的代码的用户将无法按预期访问对象的属性。
我实际上建议使用 class A
中显示的代码。您拥有的是重复 代码,而不是冗余代码,重复并不总是 不好。您只需编写 __init__
一次,并且为每个实例变量保留一个赋值是很好的文档(明确且清晰),用于说明您的 class 期望的实例变量。
不过要记住的一件事是 太多 您可以初始化为不同参数的变量可能表明您的 class 需要重新设计。将一些单独的参数分组到单独的列表、dict 甚至额外的 classes 中是否更有意义?