从其他语言开始,我习惯于编写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
有什么用?
答案 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__
,您几乎永远不会覆盖它。