【问题标题】:Why to put a property variable in the constructor为什么要在构造函数中放置一个属性变量
【发布时间】:2019-10-13 21:29:47
【问题描述】:

我习惯用其他语言编写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 中有什么用处?

【问题讨论】:

  • 顺便说一句 print('I got the value:' & self._value) 应该是 print('I got the value: %d' % self._value) 我猜。
  • “在属性中设置”是什么意思?该属性不可设置。
  • 很容易理解,我们使用@property来获取动态值。最简单的示例可能是根据当前时间获取值。它不是静态值,因此您不能只在初始化期间分配它。

标签: python oop constructor properties


【解决方案1】:

因为@property 不是变量的装饰器,它是一个decorator for function,它允许它像一个属性一样工作。你仍然需要创建一个类变量来使用@property修饰的函数:

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

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

      Python 对象不是基于结构的(如 C++ 或 Java),它们是基于字典的(如 Javascript)。这意味着实例属性是动态的(您可以在运行时添加新属性或删除现有属性),并且不是在类级别而是在实例级别定义的,并且通过分配给它们来定义它们非常简单。虽然从技术上讲,它们可以在代码中的任何位置(甚至在类之外)定义,但惯例(和良好做法)是在初始化程序(__init__ 方法 - 真正的构造函数被命名为 @ 987654323@ 但几乎没有理由重写它)以明确给定类的实例应该具有哪些属性。

      注意这里使用的术语“属性”——在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 语法没有什么神奇之处(也不是特定于属性),它只是语法糖,所以给定了一个“装饰器”函数:

       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 中不起作用(就像尝试在 Java FWIW 中编写 Python 一样)。

      注意:只是为了完整起见:Python 对象 可以 实际上通过使用 __slots__ 来“基于结构” - 但这里的目的不是防止动态添加属性(这只是副作用),但要使这些类的实例在大小上“更轻”(当您知道在给定时间将拥有数千或更多实例时,这很有用)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-07
        • 1970-01-01
        • 2013-09-18
        • 2017-05-30
        • 2011-07-06
        • 2012-01-17
        相关资源
        最近更新 更多