Python устанавливает атрибуты при создании объекта в __new__ - PullRequest
0 голосов
/ 25 января 2019

При использовании __new__ для настройки создания метакласса мы можем передавать атрибуты методу type () .__ new__, который будет установлен для объекта до его возвращения, например,

class Foo(type):    
    def __new__(cls, name, bases, attrs):
        attrs['customAttr'] = 'someVal' 
        return type.__new__(cls, name, bases, attrs)

Так что:

>> Foo.__dict__
{'customeAttr': 'someVal', ...}

Однако я не знаю, как сделать то же самое для обычного (не мета) класса, что вызывает проблему при использовании __setattr __:

class Bar(object):
    def __new__(cls, someVal):
        obj = object().__new__(cls) # cant pass custom attrs
        obj.customAttr = someVal # obj is already a Bar and invokes __setattr__
        return obj

    def __setattr__(*args): raise Exception('read-only class')

Так что к сожалению:

>>> Bar(42)
...
Exception: read-only class

В __new__ из Bar я возвращаю полноценный экземпляр класса из object (), и любой доступ к атрибуту проходит через обычные правила поиска, в этом случае вызывая __setattr__. Metaclass Foo избегает этого, так как type () будет устанавливать атрибуты перед возвратом экземпляра во время низкоуровневого создания, тогда как object () не будет.

Есть ли способ передачи атрибутов в object () или другой тип, который я могу использовать в качестве экземпляра, возвращенного из __new__, который позволяет устанавливать атрибуты до того, как он станет полным экземпляром класса? Меня не интересуют такие решения, как установка __class__ после создания экземпляра.

1 Ответ

0 голосов
/ 25 января 2019

Вы должны явно обойти __setattr__ своего класса, вызвав super или root object __setattr__. Таким образом, вы измените:

obj.customAttr = someVal

до:

object.__setattr__(obj, 'customAttr', someVal)

Менее общий подход (не относится к __slots__ основанным на классах) - это непосредственное присвоение __dict__ с использованием dict операций:

obj.__dict__['customAttr'] = someVal  # Equivalently: vars(obj)['customAttr'] = someVal

Первый подход - это то, что теперь использует __slots__ -ed uuid.UUID; прежде чем он стал __slots__ -едом, он использовал второй подход . В обоих случаях это было необходимо, потому что они использовали один и тот же трюк __setattr__, чтобы сделать тип как можно более неизменным (без необходимости создавать подклассы tuple, a la typing.NamedTuple).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...