Python 调用对象时临时更改全局变量
我试图理解如何在调用对象时临时更改其属性,以及在不调用对象时保持原始值 让我用一些代码来描述这个问题:Python 调用对象时临时更改全局变量,python,python-3.x,object,Python,Python 3.x,Object,我试图理解如何在调用对象时临时更改其属性,以及在不调用对象时保持原始值 让我用一些代码来描述这个问题: class DateCalc: DEFAULT= "1/1/2001" def __init__(self, day=DEFAULT): self.day= day def __call__(self, day=DEFAULT): self.day= day return self def getday(self): return self.day 如果用户在传递另一
class DateCalc:
DEFAULT= "1/1/2001"
def __init__(self, day=DEFAULT):
self.day= day
def __call__(self, day=DEFAULT):
self.day= day
return self
def getday(self):
return self.day
如果用户在传递另一个值时调用getday方法
i、 e 2002年2月2日,自我日设置为2002年2月2日。但是,我希望能够在方法调用后将self.day恢复为1/1/2001的原始值:
d_obj = DateCalc()
d_obj.getday() == "1/1/2001"
True
d_obj().getday() == "1/1/2001"
True
another_day_str = "2/2/2002"
d_obj(another_day_str).getday()
返回
"2/2/2002"
"2/2/2002"
但是当我运行以下命令时
d_obj.getday()
返回
"2/2/2002"
"2/2/2002"
我想知道什么是还原值的正确方法,而不需要在每个方法调用中包含代码。其次,在调用对象时也应该如此。例如:
d_obj().getday()
应该回来
"1/1/2001"
我以为调用魔术方法的装饰师会在这里工作,但我真的不确定从哪里开始
任何帮助都将不胜感激您似乎想要一种类似于上下文管理器的行为:要在有限的时间内修改属性,请使用更新的属性,然后还原为原始属性。您可以通过让
\uuuu call\uuuu
返回一个上下文管理器来实现这一点,然后您可以在和
块中使用上下文管理器,如下所示:
d_obj = DateCalc()
print(d_obj.getday()) # 1/1/2001
with d_obj('2/2/2002'):
print(d_obj.getday()) # 2/2/2002
print(d_obj.getday()) # 1/1/2001
有几种方法可以创建这样的上下文管理器。最简单的方法是在调用中使用嵌套方法,并用以下内容装饰它:
您也可以为此使用一个完全成熟的嵌套类,但我不建议您使用它,除非您有一些非常复杂的需求。我只是提供完整性:
def __call__(self, day=DEFAULT):
class Context:
def __init__(self, inst, new):
self.inst = inst
self.old = inst.day
self.new = new
def __enter__(self):
self.inst.day = self.new
def __exit__(self, *args):
self.inst.day = self.old
return Context(self, day)
也应该考虑编写<代码> GETEDA/COD>一个属性,特别是如果它是只读属性的时候。
另一种选择是让您的方法接受不同的值:
def getday(self, day=None):
if day is None:
day = self.day
return day
这实际上是一个相当常见的习惯用法。您似乎想要一种类似于上下文管理器的行为:要在有限的时间内修改属性,请使用更新的属性,然后还原为原始属性。您可以通过让\uuuu call\uuuu
返回一个上下文管理器来实现这一点,然后您可以在和
块中使用上下文管理器,如下所示:
d_obj = DateCalc()
print(d_obj.getday()) # 1/1/2001
with d_obj('2/2/2002'):
print(d_obj.getday()) # 2/2/2002
print(d_obj.getday()) # 1/1/2001
有几种方法可以创建这样的上下文管理器。最简单的方法是在调用中使用嵌套方法,并用以下内容装饰它:
您也可以为此使用一个完全成熟的嵌套类,但我不建议您使用它,除非您有一些非常复杂的需求。我只是提供完整性:
def __call__(self, day=DEFAULT):
class Context:
def __init__(self, inst, new):
self.inst = inst
self.old = inst.day
self.new = new
def __enter__(self):
self.inst.day = self.new
def __exit__(self, *args):
self.inst.day = self.old
return Context(self, day)
也应该考虑编写<代码> GETEDA/COD>一个属性,特别是如果它是只读属性的时候。
另一种选择是让您的方法接受不同的值:
def getday(self, day=None):
if day is None:
day = self.day
return day
这实际上是一个相当常见的习惯用法。由于您可能并不真的想修改对象的属性以获得定义不好的间隔,因此您需要返回或以其他方式创建另一个对象
最简单的情况是,您有两个单独的对象,并且根本没有调用方法:
d1_obj = DateCalc()
d2_obj = DateCalc('2/2/2002')
print(d1_obj.getday()) # 1/1/2001
print(d2_obj.getday()) # 2/2/2002
如果您知道在原始情况下要在何处使用d_obj
vsd_obj()
,那么您也清楚地知道在此版本中在何处使用d1_obj
vsd2_obj
如果DateCalc
实际表示一个非常复杂的对象,并且该对象具有许多您不想更改的属性,则这可能不足以满足这种情况。在这种情况下,您可以让\uuuuu call\uuuu
方法返回一个单独的对象,该对象智能地复制您想要的原始文件的部分
对于一个简单的例子,这可能只是
def __call__(self, day=DEFAULT):
return type(self)(day)
如果对象变得足够复杂,则需要创建代理。代理是将大部分实现细节转发给另一个对象的对象。是一个具有高度定制实现的代理示例
在您的特定情况下,您有两个要求:
代理必须存储所有重写的属性
代理必须从原始对象获取所有未重写的属性
代理必须将自身作为self
参数传递给调用的任何(至少是非特殊)方法
您可以随心所欲地处理这个问题(在这种情况下,请查看如何正确实现代理对象,如)。下面是一个相当简单的例子:
# Assume that there are many fields like `day` that you want to modify
class DateCalc:
DEFAULT= "1/1/2001"
def __init__(self, day=DEFAULT):
self.day= day
def getday(self):
return self.day
def __call__(self, **kwargs):
class Proxy:
def __init__(self, original, **kwargs):
self._self_ = original
self.__dict__.update(kwargs)
def __getattribute__(self, name):
# Don't forward any overriden, dunder or quasi-private attributes
if name.startswith('_') or name in self.__dict__:
return object.__getattribute__(self, name)
# This part is simplified:
# it does not take into account __slots__
# or attributes shadowing methods
t = type(self._self_)
if name in t.__dict__:
try:
return t.__dict__[name].__get__(self, t)
except AttributeError:
pass
return getattr(self._self_, name)
return Proxy(self, **kwargs)
代理将完全按照您所希望的方式工作:它从原始对象转发您在\uuuuu调用中未重写的任何值。有趣的是,它将实例方法绑定到代理对象,而不是原始对象,这样getday
就可以用一个self
调用,该self
中包含重写的值:
d_obj = DateCalc()
print(type(d_obj)) # __main__.DateCalc
print(d_obj.getday()) # 1/1/2001
d2_obj = d_obj(day='2/2/2002')
print(type(d2_obj)) # __main__.DateCalc.__call__.<locals>.Proxy
print(d2_obj.getday()) # 2/2/2002
这里的问题是代理版本的day
只是一个常规的可写属性,而不是只读属性。如果这对您来说是一个问题,那么在代理上适当地实现\uuuu setattr\uuuu
将留给读者作为练习。因为您可能并不真的想修改对象的属性以获得定义不好的间隔,所以您需要返回或以其他方式创建一个不同的对象
最简单的情况是,您有两个单独的对象,并且根本没有调用方法:
d1_obj = DateCalc()
d2_obj = DateCalc('2/2/2002')
print(d1_obj.getday()) # 1/1/2001
print(d2_obj.getday()) # 2/2/2002
如果您知道在原始情况下要在何处使用d_obj
vsd_obj()
,那么您也清楚地知道在此版本中在何处使用d1_obj
vsd2_obj
如果DateCalc
实际表示一个非常复杂的对象,并且该对象具有许多您不想更改的属性,则这可能不足以满足这种情况。在这种情况下,您可以让\uuuuu call\uuuu
方法返回一个单独的对象,该对象智能地复制您想要的原始文件的部分
对于一个简单的例子,这可能只是
def __call__(self, day=DEFAULT):
return type(self)(day)
如果对象变得足够复杂,则需要创建代理。代理是转发大多数实现的对象A