使用方法的 Django 猴子修补模型似乎作为实例方法正常工作——我是不是遗漏了什么?
Django monkey patching model with method seems to work correctly as instance method - am I missing something?
我正在使用 Django 3.2,我不得不根据一些条件逻辑对模型进行猴子修补。
myapp/models.py
class Foo(models.Model):
# fields ...
# methods ...
pass
在控制台中
from myapp.models import Foo
def something(obj):
return obj.id
# Monkey patching the class (model)
setattr(Foo, 'mysomething', something)
# Fetch an instance form db
f = Foo.objects.all().first()
f.mysomething()
# return 1 (correct)
令我惊讶的是,something 方法不接受参数,相反,它似乎使用定义 (obj) 中指定的参数作为 self
的同义词 - 因此,方法的行为类似于 实例方法 .
任何人都可以解释发生这种情况的技术原因 - 这种记录在案的(因此是一致的)行为是我可以在我的代码中依赖的吗?
这是预期的一致行为。这样做:
setattr(Foo, 'mysomething', something)
在功能上等同于这样做(只是你稍后再做,而不是在定义 class 时做):
class Foo:
def mysomething(self):
return something(self)
在 Python 中,在 class 上定义一个没有任何装饰器的方法意味着它是一个实例方法——即,该方法的第一个参数将是实例本身。通过 setattr
定义方法不会改变这一事实 - 它只是一种不太明显的方法。
考虑一下这个替代方案,它说明了这一点:
# Now we wrap `something` in `staticmethod`
setattr(Foo, 'mysomething', staticmethod(something))
相当于:
class Foo:
@staticmethod
def mysomething():
return something()
现在,因为它是作为静态方法添加的,所以当您调用它时,它不会被传递给 class 的实例,如果您尝试调用 Foo().mysomething()
:
TypeError: something() missing 1 required positional argument: 'obj'
我正在使用 Django 3.2,我不得不根据一些条件逻辑对模型进行猴子修补。
myapp/models.py
class Foo(models.Model):
# fields ...
# methods ...
pass
在控制台中
from myapp.models import Foo
def something(obj):
return obj.id
# Monkey patching the class (model)
setattr(Foo, 'mysomething', something)
# Fetch an instance form db
f = Foo.objects.all().first()
f.mysomething()
# return 1 (correct)
令我惊讶的是,something 方法不接受参数,相反,它似乎使用定义 (obj) 中指定的参数作为 self
的同义词 - 因此,方法的行为类似于 实例方法 .
任何人都可以解释发生这种情况的技术原因 - 这种记录在案的(因此是一致的)行为是我可以在我的代码中依赖的吗?
这是预期的一致行为。这样做:
setattr(Foo, 'mysomething', something)
在功能上等同于这样做(只是你稍后再做,而不是在定义 class 时做):
class Foo:
def mysomething(self):
return something(self)
在 Python 中,在 class 上定义一个没有任何装饰器的方法意味着它是一个实例方法——即,该方法的第一个参数将是实例本身。通过 setattr
定义方法不会改变这一事实 - 它只是一种不太明显的方法。
考虑一下这个替代方案,它说明了这一点:
# Now we wrap `something` in `staticmethod`
setattr(Foo, 'mysomething', staticmethod(something))
相当于:
class Foo:
@staticmethod
def mysomething():
return something()
现在,因为它是作为静态方法添加的,所以当您调用它时,它不会被传递给 class 的实例,如果您尝试调用 Foo().mysomething()
:
TypeError: something() missing 1 required positional argument: 'obj'