Декоратор класса Python "self" кажется неправильным - PullRequest
0 голосов
/ 16 мая 2018

Я пытаюсь выяснить, как я могу изменить функциональность __setattr__ класса с помощью декоратора класса, но у меня возникают проблемы при попытке доступа к self внутри функции, которая заменяет __setattr__.Если я изменю проблматическую строку так, чтобы не получать доступ к self, например, заменив ее на val = str(val), я получу ожидаемое поведение.

Я вижу подобные проблемы в других вопросах здесь, но они используют другой подход, гдекласс используется в качестве декоратора.Мой подход, представленный ниже, кажется менее сложным, поэтому я хотел бы сохранить его таким, если это возможно.

Почему a не может быть определено в self / foo там, где я ожидаю?

# Define the function to be used as decorator
# The decorator function accepts the relevant fieldname as argument
# and returns the function that wraps the class
def field_proxied(field_name):

    # wrapped accepts the class (type) and modifies the functionality of
    # __setattr__ before returning the modified class (type)
    def wrapped(wrapped_class):
        super_setattr = wrapped_class.__setattr__

        # The new __setattr__ implementation makes sure that given an int,
        # the fieldname becomes a string of that int plus the int in the
        # `a` attribute
        def setattr(self, attrname, val):
            if attrname == field_name and isinstance(val, int):
                val = str(self.a + val)  # <-- Crash. No attribute `a`
                super_setattr(self, attrname, val)
        wrapped_class.__setattr__ = setattr
        return wrapped_class
    return wrapped

@field_proxied("b")
class Foo(object):
    def __init__(self):
        self.a = 2
        self.b = None

foo = Foo()
# <-- At this point, `foo` has no attribute `a`
foo.b = 4
assert foo.b == "6"  # Became a string

1 Ответ

0 голосов
/ 16 мая 2018

Проблема проста, вам просто нужно изменить одну строку.

def setattr(self, attrname, val):
    if attrname == field_name and isinstance(val, int):
        val = str(self.a + val)
    super_setattr(self, attrname, val)  # changed line

Причина в том, что в исходном методе вы будете вызывать super_setattr только тогда, когда attrname == field_name. Так что self.a = 2 в __init__ вообще не работает как "a" != "b".

...