有没有办法知道对象实例化的上下文?到目前为止,我一直在搜索并尝试inspect
模块(currentcontext
),效果不佳。
例如
class Item:
pass
class BagOfItems:
def __init__(self):
item_1 = Item()
item_2 = Item()
item_3 = Item()
我想在item_3
的实例化中引发异常(因为它在BagOfItems
之外),而在item_1
和item_2
中没有这样做。我不知道元类是否可以解决这个问题,因为问题出现在实例化而非声明时。
持有者类(BagOfItems
)无法实现检查,因为当Item
实例化在其外部发生时,将不会进行检查。
答案 0 :(得分:1)
当您使用Item()
之类的内容实例化对象时,您基本上正在执行type(Item).__call__()
,它会在调用序列中的某个位置调用Item.__new__()
和Item.__init__()
。这意味着,如果您浏览导致Item.__init__()
的调用序列,您最终会找到不在Item
或type(Item)
中的代码。你的要求是堆栈中第一个这样的“上下文”以某种方式属于BagOfItem
。
在一般情况下,您无法确定包含负责堆栈框架 1 的方法的类。但是,如果您要求只能在类方法中实例化,则不再使用“一般情况”。方法的第一个参数始终是类的实例。因此,我们可以向上移动堆栈跟踪,直到找到一个方法调用,其第一个参数既不是Item
的实例,也不是type(Item)
的子类。如果框架有参数(即,它不是模块或类体),并且第一个参数是BagOfItems
的实例,请继续。否则,引发错误。
请记住,像type(Item).__call__()
这样的非显而易见的调用可能根本不会出现在堆栈跟踪中。我只是想为他们做好准备。
支票可以这样写:
import inspect
def check_context(base, restriction):
it = iter(inspect.stack())
next(it) # Skip this function, jump to caller
for f in it:
args = inspect.getargvalues(f.frame)
self = args.locals[args.args[0]] if args.args else None
# Skip the instantiating calling stack
if self is not None and isinstance(self, (base, type(base))):
continue
if self is None or not isinstance(self, restriction):
raise ValueError('Attempting to instantiate {} outside of {}'.format(base.__name__, restriction.__name__))
break
然后,您可以将其嵌入Item.__init__
:
class Item:
def __init__(self):
check_context(Item, BagOfItems)
print('Made an item')
class BagOfItems:
def __init__(self):
self.items = [Item(), Item()]
boi = BagOfItems()
i = Item()
结果将是:
Made an item
Made an item
Traceback (most recent call last):
...
ValueError: Attempting to instantiate Item outside of BagOfItems
<强>注意事项强>
所有这些都阻止您在另一个类的方法之外调用一个类的方法。它无法在staticmethod或classmethod或模块范围内正常工作。如果你有动力,你可以解决这个问题。我已经学到了更多关于内省和堆栈跟踪的知识,所以我会把它称为一天。这应该足以让你开始,或者更好,向你展示为什么你不应该继续这条道路。
此处使用的功能可能是特定于CPython的。我真的不太了解检查能够肯定地告诉我。我确实试图尽可能地远离CPython特有的功能。
<强>参考强>
<子>
1. Python: How to retrieve class information from a 'frame' object?
2. How to get value of arguments passed to functions on the stack?
3. Check if a function is a method of some object
4. Get class that defined method
5. Python文档:inspect.getargvalues
6. Python文档:inspect.stack
子>