使用方法的 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'