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

rue*_*edi 0 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)
Run Code Online (Sandbox Code Playgroud)

在我处理的几乎每个例子中,属性变量都在self._value像这样的构造函数中

Class MyClass:
    def __init__(self, value = 0):
        self._value = value
Run Code Online (Sandbox Code Playgroud)

对我来说这毫无意义,因为您想在属性中设置它。任何人都可以向我解释将 放在value variable中的用途是 constructor 什么?

bru*_*ers 5

Python 对象不是基于结构的(如 C++ 或 Java),它们是基于字典的(如 Javascript)。这意味着实例属性是动态的(您可以在运行时添加新属性或删除现有属性),并且不是在类级别定义的,而是在实例级别定义的,并且通过分配给它们非常简单地定义它们。虽然技术上可以在代码中的任何位置(甚至在类之外)定义它们,但约定(和良好实践)是在初始值设定项中定义它们(最终为默认值)(__init__方法 - 真正的构造函数已命名,__new__但有很多很少有理由覆盖它)以明确给定类的实例应该具有哪些属性。

注意这里使用的术语“属性”——在python中,我们不谈论“成员变量”或“成员函数”,而是谈论“属性”和“方法”。实际上,由于 Python 类也是对象(type类的实例或类的子类),它们也有属性,所以我们有实例属性(每个实例)和类属性(属于类对象本身,并且是在实例之间共享)。可以在实例上查找类属性,只要它没有被同名的实例属性遮蔽即可。

此外,由于 Python 函数也是对象(提示:在 Python 中,一切——你可以放在赋值的 RHS 上的所有东西——都是一个对象),“数据”属性和“函数”属性没有不同的命名空间,和 Python 的“方法”实际上是在类本身上定义的函数 - IOW 它们是恰好是function类型实例的类属性。由于方法需要访问实例才能对其进行处理,因此有一种特殊的机制允许“自定义”属性访问所以一个给定的对象——如果它实现了正确的接口——当它在一个实例上查找但在类上解析时可以返回除自身之外的其他东西。这种机制被函数使用,因此它们将自己变成方法(将函数和实例包装在一起的可调用对象,因此您不必将实例传递给函数),而且更普遍地作为对计算属性的支持。

这个property类是一个计算属性的通用实现,它包装了一个 getter 函数(最终是一个 setter 和一个删除器)——所以在 Python 中“属性”有一个非常具体的含义(property类本身或它的一个实例)。此外,@decorator语法没有什么神奇的(并且不是特定于属性的),它只是语法糖,因此给出了“装饰器”函数:

 def decorator(func):
     return something
Run Code Online (Sandbox Code Playgroud)

这个:

 @decorator
 def foo():
     # code here
Run Code Online (Sandbox Code Playgroud)

只是一个快捷方式:

 def foo():
     # code here

 foo = decorator(foo)
Run Code Online (Sandbox Code Playgroud)

在这里我定义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
Run Code Online (Sandbox Code Playgroud)

然后,您可能需要检查该类及其实例:

>>> 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
>>> 
Run Code Online (Sandbox Code Playgroud)

结论是:如果你希望用一门语言做任何有用的事情,你必须学习这门语言——你不能期望它像你知道的其他语言一样工作。虽然有些特性基于共同的概念(即函数、类等),但它们实际上可以以完全不同的方式实现(Python 的对象模型与 Java 的对象模型几乎没有任何共同点),因此只需尝试编写 Java(或 C 或Python 中的 C++ 等)将不起作用(就像尝试在 Java FWIW 中编写 Python 一样)。

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