Django monkey патч модели с методом, кажется, работает правильно как метод экземпляра - я что-то упускаю?

Я использую 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)

My surprise is the fact that the something method is not accepting an argument, and instead, seems to be using the argument specified in the definition (obj) as a synonym for self - and therefore, the method is acting like an instance method.

Может ли кто-нибудь объяснить техническую причину, почему это происходит - и является ли это документированным (и, следовательно, последовательным) поведением, на которое я могу положиться в своем коде?

Это ожидаемое и последовательное поведение. Поступая так:

setattr(Foo, 'mysomething', something)

функционально эквивалентно этому (только вы делаете это позже, а не в момент определения класса):

class Foo:

    def mysomething(self):
        return something(self)

В Python определение метода на классе без декоратора подразумевает, что это метод экземпляра - то есть, что первым аргументом метода будет сам экземпляр. Определение метода через setattr не меняет этого факта - это просто менее очевидный способ сделать это.

Рассмотрим эту альтернативу, которая иллюстрирует суть:

# Now we wrap `something` in `staticmethod`
setattr(Foo, 'mysomething', staticmethod(something))

эквивалентно:

class Foo:

    @staticmethod
    def mysomething():
        return something()

Теперь, поскольку он был добавлен как статический метод, ему не будет передан экземпляр класса, когда вы его вызовете, и вы получите ошибку, если попытаетесь вызвать Foo().mysomething():

TypeError: something() missing 1 required positional argument: 'obj'
Вернуться на верх