[英]A way to access context manager variable from class instance in Python?
我会从其他模块中定义的某些类访问上下文中定义的一些变量,就像我在以下内容中进行了超级简化:
在 main.py 中
import class_def1
import class_def2
class Starter(object):
#context manager
def __init__(self):
self.att = 'Myfirst'
def __enter__(self):
return self.att
def __exit__(self, exc_type, exc_val, exc_tb):
pass
with Starter() as f:
for name in ('a', 'b', 'c'):
for number in range(2):
c1 = class_def1.MyClass1()
print(c1.attr1)
c2 = class_def2.MyClass2()
print(c2.attr2)
而在 class_def1.py 中:
class MyClass1(object):
global f
global number
def __init__(self):
self.attr1 = ','.join([f,str(number)])
在 class_def2.py 中:
class MyClass2(object):
def __init__(self):
global f
global name
global number
self.attr2 = ','.join([f,name,str(number)])
这段代码不起作用,我还想避免同时使用“全局”和init输入参数,因为我有很多要“传递”的变量......也许我要求太多了。 有什么建议吗?
新的考虑
考虑到使用数据类作为 DTO 的可能性(如@Niel Godfrey Ponciano 所建议的),我可以通过这种方式创建一个数据类(或者甚至可能列出其他变量并将默认值设置为零):
@dataclass
class AllInfo:
f: str
with Starter() as f:
info = AllInfo(f)
for name in (a,b,c):
info.name = name
for number in range(2):
info.number = number
c1 = class_def1.MyClass1(info)
print(c1.attr1)
c2 = class_def2.MyClass2(info)
print(c2.attr2)
但是,由于它只是存储变量的问题(不需要任何方法),因此这等效于:
class AllInfo:
def __init__(self,f):
self.f = f
(在我看来,自动添加到数据类中的repr方法无关紧要)。
但是,在这两种情况下,我都需要传递整个信息集。 可能不是这样的问题,因为它是一个指针......
关于上下文管理器Starter
的__enter__
方法,如文档中所述:
此方法返回的值绑定到使用此上下文管理器的 with 语句的 as 子句中的标识符。
返回的值是self.att
:
def __enter__(self):
return self.att # Returns the string "Myfirst"
这意味着f
的值将是字符串“Myfirst”。 如果您想要f
是整个Starter
实例,而不仅仅是属性Starter.att
,那么请return self
而不是return self.att
。
with Starter() as f: # f will be a string "Myfirst"
现在,下一步是将该上下文管理器变量(来自Starter.att
f
)以及一些迭代值name
和number
传递给您的其他类MyClass1
和MyClass2
。 执行此操作的一种标准方法是执行依赖项注入,其中我们将依赖项f
、 name
和value
注入目标客户端,即MyClass1
和MyClass2
。 简单来说,设计是MyClass1
和MyClass2
依赖于f
、 name
和value
。
你提到你不想传递给init。 但这是执行此操作的标准方法之一,也是最简单的方法。 如果它更适合您,请继续下面的解决方案 2,
class Starter(object):
#context manager
def __init__(self):
self.att = 'Myfirst'
def __enter__(self):
return self.att
def __exit__(self, exc_type, exc_val, exc_tb):
pass
class MyClass2(object):
def __init__(self, f, name, number):
text_list = [f, name, str(number)]
self.attr2 = ','.join(text_list)
with Starter() as f:
for name in ('a', 'b', 'c'):
for number in range(2):
# Inject all dependencies to the class
c2 = MyClass2(f, name, number)
print(c2.attr2)
输出:
Myfirst,a,0
Myfirst,a,1
Myfirst,b,0
Myfirst,b,1
Myfirst,c,0
Myfirst,c,1
如果您有很多变量,但只会使用其中的少数几个,这可能适用。 最好的用例之一是构建器设计模式。
class Starter(object):
#context manager
def __init__(self):
self.att = 'Myfirst'
def __enter__(self):
return self.att
def __exit__(self, exc_type, exc_val, exc_tb):
pass
class MyClass2(object):
def __init__(self):
self._f = None
self._name = None
self._number = None
# Here, we are using property getters and setters. Of course you can also do it by just directly
# accessing the variable from the outside, just remove the _ in the name to imply that they are
# public and not private as it is now).
@property
def f(self):
return self._f
@f.setter
def f(self, value):
self._f = value
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def number(self):
return self._number
@number.setter
def number(self, value):
self._number = value
@property
def attr2(self):
# Optionally, you can add checks to filter out null variables in the list
text_list = [self.f, self.name, str(self.number)]
return ','.join(text_list)
with Starter() as f:
for name in ('a', 'b', 'c'):
for number in range(2):
c2 = MyClass2()
# Manually inject each dependency to the class. You can add conditionals to select what
# should be injected and what shouldn't.
c2.f = f
c2.name = name
c2.number = number
print(c2.attr2)
输出:
如果您有很多变量,如@chepner 所建议的,您可以将它们全部(或按相关数据组)放入其自己的数据结构中。 您可以为此目的使用数据类(就像 C/C++ 中的struct
一样),它将充当您的数据传输对象 (DTO) 。
Python 旨在允许在不使用全局声明的情况下读取全局变量,如果函数需要修改在全局范围内声明的变量,则需要使用全局声明。 所以下面的代码应该工作
class MyClass1(object):
def __init__(self):
self.attr1 = ','.join([f,str(number)])
class MyClass2(object):
def __init__(self):
self.attr2 = ','.join([f,name,str(number)])
class Starter(object):
#context manager
def __init__(self):
self.att = 'Myfirst'
def __enter__(self):
return self.att
def __exit__(self, exc_type, exc_val, exc_tb):
pass
with Starter() as f:
for name in ('a', 'b', 'c'):
for number in range(2):
c1 = MyClass1()
print(c1.attr1)
c2 = MyClass2()
print(c2.attr2)
>>> Myfirst,0
Myfirst,a,0
Myfirst,1
Myfirst,a,1
Myfirst,0
Myfirst,b,0
Myfirst,1
Myfirst,b,1
Myfirst,0
Myfirst,c,0
Myfirst,1
Myfirst,c,1
如果这没有帮助,并且您确实想访问在另一个类的上下文中定义的某个变量,则需要在初始化时传递该变量。 像这样的东西:
class MyClass1(object):
def __init__(self,f):
self.attr1 = ','.join([f,str(number)])
class MyClass2(object):
def __init__(self,f):
self.attr2 = ','.join([f,name,str(number)])
class Starter(object):
#context manager
def __init__(self):
self.att = 'Myfirst'
def __enter__(self):
return self.att
def __exit__(self, exc_type, exc_val, exc_tb):
pass
with Starter() as f:
for name in ('a', 'b', 'c'):
for number in range(2):
c1 = MyClass1(f)
print(c1.attr1)
c2 = MyClass2(f)
print(c2.attr2)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.