我想知道是否可以检索方法使用的属性。
以下是解释我要做的事情的示例:
class Foobar(object):
bar = 123
@property
def foo(self):
return self.baz * self.bar
def get_foobar(self):
return '{} * {} = {}'.format(self.baz, self.bar, self.foo)
我希望能够知道调用Foobar().get_foobar()
需要设置self.baz
,self.bar
和self.foo
,而不会实际调用它。
我目前的做法是向get_foobar
添加属性:
def get_foobar(self):
return '{} * {} = {}'.format(self.baz, self.bar, self.foo)
get_foobar.requires = ['baz', 'bar', 'foo']
但是,我认为它有点多余,可能容易出错。
你可能想知道我为什么要这样做。
在我的具体案例中,Foobar
实际上是一个django模型。而属性实际上是从数据库中检索的字段。我创建了一个View
mixin,使我能够指定视图所需的字段。例如:
class SomeUserView(ModelMixin, View):
model = User
fields = [
'username', 'groups__name', 'permissions__id',
'orders__products__name', 'orders__products__price',
]
通过一些内省,我可以构建查询,该查询将检索视图所需的所有数据。在这种情况下,它看起来像:
User.objects.all().only(
'username', 'groups__name', 'permissions__id',
'orders__products__name', 'orders__products__price',
).select_related('groups', 'permissions', 'orders__products')
现在,fields
属性不仅可以包含字段,还可以包含实例方法,这些方法可能需要未列出的字段。假设我有:
class User(models.Model):
def __str__(self):
return '{} ({})'.format(self.username, self.email)
__str__.requires_fields = ['username', 'email']
class Permission(models.Model):
def __str__(self):
return self.name
__str__.requires_fields = ['name']
class SomeUserView(ModelMixin, View):
model = User
fields = [
'username', 'groups__name', 'permissions', '__str__',
'orders__products__name', 'orders__products__price',
]
然后,查询将是:
User.objects.all().only(
'username', 'groups__name', 'permissions__name', 'email',
'orders__products__name', 'orders__products__price',
).select_related('groups', 'permissions', 'orders__products')
这样可行,但是我想避免为每个方法设置requires_fields
属性,并且每次修改方法时都必须仔细更新它。
我对这种可能性没有很大希望,但仍然在问。
作为替代方案,我想我可以写一个装饰器,例如:
class Wrapper:
def __init__(self, obj, fields):
self.obj = obj
self.fields = set(fields)
self._used_fields = set()
def __getattribute___(self, name):
if name not in self.fields:
raise AttributeError(
"You forgot to set '{}' as required field".format(name))
self._used_fields.add(name)
return getattr(self.obj, name)
def exit_method(self):
if self.fields != self._used_fields:
warnings.warn(
"Make sure the following fields are actually needed: {}".format(
self.fields - self._used_fields))
def requires_fields(*fields):
def decorator(func):
def inner(self, *args, **kwargs):
self_wrapper = Wrapper(self, fields)
func(self_wrapper, *args, **kwargs)
self_wrapper.exit_method()
inner.__name__ = func.__name__
inner.requires_fields = fields
return decorator
@requires_fields('baz', 'bar', 'foo')
def get_foobar(self):
return '{} * {} = {}'.format(self.baz, self.bar, self.foo)
这样我就可以轻松“发现错误”。但它看起来很奇怪。 :d
答案 0 :(得分:1)
我希望能够知道调用Foobar()。get_foobar() 将要求self.baz,self.bar和self.foo设置,没有 实际上是在呼唤它。
不,当然不是,一般情况下。这是我的 foobar:
return self.foo if isHalting() else self.bar
return eval("self.foo")
但更现实的是,您有几种选择:
编译the syntax tree。检查字段节点(或您感兴趣的任何一个),然后查看它们的值。这可能很耗时。熟悉访问者模式,这可能是一种非常强大的解析方式;这就是像C或Java这样的静态语言如何引发编译错误,变量将被定义为*。
使用function.get.__closure__
检查闭包这不会帮助您检索所有字段,但可以帮助您确定哪些字段已绑定到该函数,这在某些情况下可能相关
最好的选择是使用try / except语句。根据需要设置它们的样式,例如
def foo():
s = ''
try:
s += str(self.baz)
except: raise NoBazError("No Baz!")
对我来说,这是最好的选择,因为它是最明确和最直接的选择。
*
如果您使用字段,即使是静态类型的语言也会很难,但可以检查局部变量的初始化,有时