Dynamically defining instance fields in Python classes

后端 未结 3 1468
闹比i
闹比i 2021-02-05 10:48

I am new to Python having come from mainly Java programming.

I am currently pondering over how classes in Python are instantiated.

I understand that __ini

相关标签:
3条回答
  • 2021-02-05 11:03

    This answer pertains to new-style Python classes, which subclass object. New-style classes were added in 2.2, and they're the only kind of class available in PY3.

    >>> print object.__doc__
    The most base type
    

    The class itself is an instance of a metaclass, which is usually type:

    >>> print type.__doc__
    type(object) -> the object's type
    type(name, bases, dict) -> a new type
    

    Per the above docstring, you can instantiate the metaclass directly to create a class:

    >>> Test = type('Test', (object,), {'__doc__': 'Test class'})
    >>> isinstance(Test, type)
    True
    >>> issubclass(Test, object)
    True
    >>> print Test.__doc__
    Test class
    

    Calling a class is handled by the metaclass __call__ method, e.g. type.__call__. This in turn calls the class __new__ constructor (typically inherited) with the call arguments in order to create an instance. Then it calls __init__, which may set instance attributes.

    Most objects have a __dict__ that allows setting and deleting attributes dynamically, such as self.value = 10 or del self.value. It's generally bad form to modify an object's __dict__ directly, and actually disallowed for a class (i.e. a class dict is wrapped to disable direct modification). If you need to access an attribute dynamically, use the built-in functions getattr, setattr, and delattr.

    The data model defines the following special methods for customizing attribute access: __getattribute__, __getattr__, __setattr__, and __delattr__. A class can also define the descriptor protocol methods __get__, __set__, and __delete__ to determine how its instances behave as attributes. Refer to the descriptor guide.

    When looking up an attribute, object.__getattribute__ first searches the object's class and base classes using the C3 method resolution order of the class:

    >>> Test.__mro__
    (<class '__main__.Test'>, <type 'object'>)
    

    Note that a data descriptor defined in the class (e.g. a property or a member for a slot) takes precedence over the instance dict. On the other hand, a non-data descriptor (e.g. a function) or a non-descriptor class attribute can be shadowed by an instance attribute. For example:

    >>> Test.x = property(lambda self: 10)
    >>> inspect.isdatadescriptor(Test.x)
    True
    >>> t = Test()
    >>> t.x
    10
    >>> t.__dict__['x'] = 0
    >>> t.__dict__
    {'x': 0}
    >>> t.x
    10
    
    >>> Test.y = 'class string'
    >>> inspect.isdatadescriptor(Test.y)
    False
    >>> t.y = 'instance string'
    >>> t.y
    'instance string'
    

    Use super to proxy attribute access for the next class in the method resolution order. For example:

    >>> class Test2(Test):
    ...     x = property(lambda self: 20)
    ... 
    >>> t2 = Test2()
    >>> t2.x
    20
    >>> super(Test2, t2).x
    10
    
    0 讨论(0)
  • 2021-02-05 11:14

    Attributes of Python objects are generally stored in a dictionary, just like the ones you create with {}. Since you can add new items to a dictionary at any time, you can add attributes to an object at any time. And since any type of object can be stored in a dictionary without previous declaration of type, any type of object can be stored as an attribute of an object.

    In short, my_object.abc = 42 is (often) just a shorthand for my_object.__dict__["abc"] = 42.

    It is possible to define objects without a __dict__ by defining the __slots__ attribute, or to override certain special methods and store attributes in some other way, though most of the time you shouldn't do that.

    0 讨论(0)
  • 2021-02-05 11:19

    I understand that __init__(): is like the constructor in Java.

    To be more precise, in Python __new__ is the constructor method, __init__ is the initializer. When you do SomeClass('foo', bar='baz'), the type.__call__ method basically does:

    def __call__(cls, *args, **kwargs):
        instance = cls.__new__(*args, **kwargs)
        instance.__init__(*args, **kwargs)
        return instance
    

    Generally, most classes will define an __init__ if necessary, while __new__ is more commonly used for immutable objects.

    However, sometimes python classes do not have an init() method which in this case I assume there is a default constructor just like in Java?

    I'm not sure about old-style classes, but this is the case for new-style ones:

    >>>> object.__init__
    <slot wrapper '__init__' of 'object' objects>
    

    If no explicit __init__ is defined, the default will be called.

    So to be clear, my question is in Python can we dynamically define new fields to a class during runtime like in this example

    Yes.

    >>> class A(object):
    ...     def __init__(self):
    ...         self.one_attribute = 'one'
    ...     def add_attr(self):
    ...         self.new_attribute = 'new'
    ...
    
    >>> a = A()
    >>> a.one_attribute
    'one'
    >>> a.new_attribute
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'A' object has no attribute 'new_attribute'
    >>> a.add_attr()
    >>> a.new_attribute
    'new'
    

    Attributes can be added to an instance at any time:

    >>> a.third_attribute = 'three'
    >>> a.third_attribute
    'three'
    

    However, it's possible to restrict the instance attributes that can be added through the class attribute __slots__:

    >>> class B(object):
    ...     __slots__ = ['only_one_attribute']
    ...     def __init__(self):
    ...         self.only_one_attribute = 'one'
    ...     def add_attr(self):
    ...         self.another_attribute = 'two'
    ...
    
    >>> b = B()
    >>> b.add_attr()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in add_attr
    AttributeError: 'B' object has no attribute 'another_attribute'
    

    (It's probably important to note that __slots__ is primarily intended as a memory optimization - by not requiring an object have a dictionary for storing attributes - rather than as a form of run-time modification prevention.)

    0 讨论(0)
提交回复
热议问题