为什么要在构造函数中放置属性变量

时间:2019-05-28 11:00:54

标签: python oop constructor properties

从其他语言开始,我习惯于编写class property的代码,之后我就可以访问它而不必像在constructor中那样

Class MyClass:
    def __init__(self):
        self._value = 0
    @property
    my_property(self):
        print('I got the value:' & self._value)

在几乎每个示例中,我处理属性变量的过程都在构造器self._value

Class MyClass:
    def __init__(self, value = 0):
        self._value = value

对我来说这没有意义,因为您想在属性中进行设置。谁能向我解释将value variable放入constructor有什么用?

3 个答案:

答案 0 :(得分:4)

由于@property不是变量的修饰符,因此它是decorator for function,允许其表现为属性。您仍然需要创建一个类变量以使用由@property装饰的函数:

  

@property装饰器将voltage()方法转换为具有相同名称的只读属性的“获取器”,并将电压的文档字符串设置为“获取当前电压”。 / p>      

属性对象具有可用作装饰器的getter,setter和deleter方法,这些方法创建属性的副本,并将相应的访问器函数设置为装饰函数。最好用一个例子来解释:

答案 1 :(得分:3)

Python对象不是基于结构的(例如C ++或Java),而是基于dict的(例如Javascript)。这意味着实例属性是动态的(您可以在运行时添加新属性或删除现有属性),并且不是在类级别而是在实例级别定义的,并且通过分配它们可以非常简单地进行定义。尽管可以从技术上在代码中的任何地方(甚至在类外部)定义它们,但惯例(和良好做法)是在初始化器(__init__方法中定义它们(最终为默认值),真正的构造函数是命名为__new__,但几乎没有理由覆盖它)来弄清楚给定类的实例应该具有哪些属性。

请注意此处使用术语“属性”-在python中,我们不是在谈论“成员变量”或“成员函数”,而是在谈论“属性”和“方法”。实际上,由于Python类也是对象(type类的实例或它的子类),所以它们也具有属性,因此我们具有实例属性(按实例)和类属性(属于该类)对象本身,并在实例之间共享)。可以在实例上查找类属性,只要它没有被相同名称的实例属性遮盖即可。

此外,由于Python函数也是对象(提示:在Python中,所有内容-您可以放置​​在分配的RHS上的所有内容-都是对象),因此没有用于“数据”属性和“函数”的独特命名空间属性和Python的“方法”实际上是在类本身上定义的函数-IOW,它们是恰好是function类型实例的类属性。由于方法需要访问实例才能在其上进行操作,there's a special mechanism that allow to "customize" attribute access因此,给定对象(如果实现了正确的接口)可以在实例上查找但在类上解析时返回除自身之外的其他值。函数使用这种机制,因此它们可以将自身变成方法(将函数和实例包装在一起的可调用对象,因此您不必将实例传递给函数),但是更广泛地用作对计算属性的支持。

property类是计算属性的通用实现,包装了getter函数(最终是setter和Deleter)-因此在Python中,“属性”具有非常特殊的含义(property类本身或其实例)。同样,@decorator语法也没有什么神奇的(并且不是特定于属性的),它只是语法糖,因此可以使用“ decorator”函数:

 def decorator(func):
     return something

此:

 @decorator
 def foo():
     # code here

只是以下操作的快捷方式:

 def foo():
     # code here

 foo = decorator(foo)

在这里,我将decorator定义为一个函数,但是可以使用任何可调用对象(“可调用”对象是定义__call__魔术方法的类的实例)来代替-和Python类是可调用的(甚至实际上是通过调用您实例化的类)。

回到您的代码:

# in py2, you want to inherit from `object` for
# descriptors and other fancy things to work.
# this is useless in py3 but doesn't break anything either...

class MyClass(object):

    # the  `__init__` function will become an attribute
    # of the `MyClass` class object

    def __init__(self, value=0):
        # defines the instance attribute named `_value`
        # the leading underscore denotes an "implementation attribute"
        # - something that is not part of the class public interface
        # and should not be accessed externally (IOW a protected attribute)
        self._value = value

    # this first defines the `my_property` function, then
    # pass it to `property()`, and rebinds the `my_property` name
    # to the newly created `property` instance. The `my_property` function
    # will then become the property's getter (it's `fget` instance attribute)
    # and will be called when the `my_property` name is resolved on a `MyClass` instance

    @property
    my_property(self):
        print('I got the value: {}'.format(self._value))
        # let's at least return something
        return self._value

然后您可能要同时检查该类及其实例:

>>> print(MyClass.__dict__)
{'__module__': 'oop', '__init__': <function MyClass.__init__ at 0x7f477fc4a158>, 'my_property': <property object at 0x7f477fc639a8>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}
>>> print(MyClass.my_property)
<property object at 0x7f477fc639a8>
>>> print(MyClass.my_property.fget)
<function MyClass.my_property at 0x7f477fc4a1e0>
>>> m = MyClass(42)
>>> print(m.__dict__)
{'_value': 42}
>>> print(m.my_property)
I got the value: 42
42
>>> 

结论:如果您希望对某种语言有用,那么您必须学习这种语言-您不能仅仅期望它能像您所知道的其他语言一样工作。尽管某些功能基于共同的概念(例如,函数,类等),但实际上它们可以以完全不同的方式实现(Python的对象模型与Java的对象模型几乎没有任何共同点),因此只需尝试编写Java(或C或C或Python中的C ++等)将无法正常工作(就像尝试用Java FWIW编写Python)一样。

NB:仅出于完整性考虑:实际上,可以使用__slots__使Python对象 成为“基于结构的”-但此处的目的不是防止动态添加属性(但这只是一个副作用),但是可以使这些类的实例的大小“更轻”(当您知道在给定的时间将要拥有成千上万个实例时,这很有用)。

答案 2 :(得分:2)

我猜您来自诸如C ++或Java之类的语言,通常将属性设为私有,然后为它们编写显式的getter和setter吗?在Python中,除了约定以外没有私有的东西,也不需要为只需要按原样编写和读取的变量编写getter和setter方法。如果您想添加其他行为(例如日志记录访问权限),或者想要拥有可以像真实属性一样访问的伪属性,则可以使用@property和相应的setter装饰器。您可能有一个Circle类,它是由半径定义的,但您可以为直径定义一个@property,以便仍然可以编写circle.diameter。

更具体地说,您的问题:如果要在创建对象时设置属性,则希望将该属性作为初始化程序的参数。您不想创建一个空对象,然后立即用属性填充它,因为这会产生很多干扰,并使代码的可读性降低。

顺便说一句:__init__实际上不是构造函数。 Python对象的构造函数是__new__,您几乎永远不会覆盖它。