python - 一种从 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)])
这段代码不起作用,我也想避免使用“全局”和输入参数来初始化,因为我有很多变量要“传递”......也许我要求太多了。有什么建议吗?
新的考虑
考虑到使用数据类作为 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方法不相关)。
但是,在这两种情况下,我都需要传递整个信息集。可能不是这样的问题,因为它是一个指针......
解决方案
关于__enter__
上下文管理器的方法Starter
,如文档中所述:
此方法返回的值绑定到使用此上下文管理器的 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"
现在,下一步是将上下文管理器变量(f
来自Starter.att
)与一些迭代值一起name
传递number
给您的其他类MyClass1
和MyClass2
. 执行此操作的一种标准方法是执行依赖项注入,其中我们将依赖项f
、name
和注入value
到目标客户端,即MyClass1
和MyClass2
。简单来说,设计就是MyClass1
并且MyClass2
取决于f
、name
和value
。
解决方案 1:使用构造函数注入
你提到你不想传递给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
解决方案 2:使用 setter 注入
如果您有很多变量,但只会使用其中的一部分,那么这可能是适用的。最好的用例之一是构建器设计模式。
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)
输出:
- 与解决方案 1 相同
如果您有很多变量,正如@chepner 所建议的那样,您可以将它们全部(或按相关数据组)放入其自己的数据结构中。您可以为此目的使用数据类(就像struct
在 C/C++ 中一样),它将充当您的数据传输对象 (DTO)。
推荐阅读
- ios - 更改标题 Swift 的颜色
- r - 对 R 中向量的连续元素应用 t.test
- android - 深度链接 - Https 意外重定向到 chrome
- java - org.apache.wicket.protocol.http.PageExpiredException--在 apache wicket 中关闭模式窗口 (window.close(target))
- gradle - 无法使用 Gradle 安装运行构建操作
- r - 在 R 中找不到对象“tibble_update_attrs”的故障排除
- linux - 如何使用 sed 命令 (Linux) 在文本中插入内容?
- javascript - 如果通过从网站(html 或 javascript)检测它的包名称从 android 查看页面,重定向到特定的 url?
- discord.js - TypeError [INVALID_TYPE]:提供的选项不是对象
- php - Laravel 属于ToMany 关系