理解 __get__ 和 __set__ 以及 Python 描述符
我试图了解Python的描述符是什么以及它们有什么用处。我理解它们是如何工作的,但我有疑问。考虑以下代码:
class Celsius(object):
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
class Temperature(object):
celsius = Celsius()
-
为什么我需要描述符类?
-
这里的
实例
和所有者
是什么? (在__get__
中)。这些参数的用途是什么? -
我如何调用/使用这个例子?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
描述符是 Python 的
property
类型的实现方式。描述符只需实现__get__
、__set__
等,然后添加到其定义中的另一个类中(就像上面对Temperature类所做的那样)。例如:访问您为其分配描述符的属性(上例中的
celsius
)会调用适当的描述符方法。__get__
中的instance
是类的实例(因此上面的__get__
将接收temp
,而Owner
是具有描述符的类(因此它是Temperature
)。您需要使用描述符类来封装支持它的逻辑,这样,如果描述符用于。缓存一些昂贵的操作(例如),它可以将值存储在其自身上而不是其类上。
官方Python文档包含一个关于描述符的文章,更详细地介绍了它们的工作原理,包括几个示例:
编辑:正如 jchl 在评论中指出的,如果您只是尝试
Temperature.celsius,
实例
将为无
。The descriptor is how Python's
property
type is implemented. A descriptor simply implements__get__
,__set__
, etc. and is then added to another class in its definition (as you did above with the Temperature class). For example:Accessing the property you assigned the descriptor to (
celsius
in the above example) calls the appropriate descriptor method.instance
in__get__
is the instance of the class (so above,__get__
would receivetemp
, whileowner
is the class with the descriptor (so it would beTemperature
).You need to use a descriptor class to encapsulate the logic that powers it. That way, if the descriptor is used to cache some expensive operation (for example), it could store the value on itself and not its class.
The official Python documentation includes an article about descriptors that walks through how they work in more detail, including several examples.
EDIT: As jchl pointed out in the comments, if you simply try
Temperature.celsius
,instance
will beNone
.它使您可以额外控制属性的工作方式。例如,如果您习惯了 Java 中的 getter 和 setter,那么 Python 就是这样做的。一个优点是它对用户来说就像一个属性(语法没有变化)。因此,您可以从普通属性开始,然后当您需要做一些奇特的事情时,切换到描述符。
属性只是一个可变值。描述符允许您在读取或设置(或删除)值时执行任意代码。因此,您可以想象使用它来将属性映射到数据库中的字段,例如,一种 ORM。
另一种用途可能是通过在 __set__ 中抛出异常来拒绝接受新值 - 有效地使“属性”只读。
这是非常微妙的(也是我在这里写一个新答案的原因 - 我在想知道同样的事情时发现了这个问题,但没有发现现有的答案那么好)。
描述符在类上定义,但通常从实例调用。当从实例调用它时,
instance
和owner
都会被设置(并且您可以从instance
计算出owner
,因此似乎有点毫无意义)。但是当从类中调用时,只有owner
被设置 - 这就是它存在的原因。只有
__get__
才需要它,因为它是唯一可以在类上调用的函数。如果设置类值,则设置描述符本身。删除也是如此。这就是为什么那里不需要owner
的原因。好吧,这里有一个使用类似类的很酷的技巧:(
我使用的是 Python 3;对于 python 2,您需要确保这些划分是
/ 5.0
和/ 9.0
)。这给出了:现在还有其他可以说更好的方法来在 python 中实现相同的效果(例如,如果 celsius 是一个属性,这是相同的基本机制,但将所有源放在Temperature类中),但这显示了可以做什么...
It gives you extra control over how attributes work. If you're used to getters and setters in Java, for example, then it's Python's way of doing that. One advantage is that it looks to users just like an attribute (there's no change in syntax). So you can start with an ordinary attribute and then, when you need to do something fancy, switch to a descriptor.
An attribute is just a mutable value. A descriptor lets you execute arbitrary code when reading or setting (or deleting) a value. So you could imagine using it to map an attribute to a field in a database, for example – a kind of ORM.
Another use might be refusing to accept a new value by throwing an exception in
__set__
– effectively making the "attribute" read only.This is pretty subtle (and the reason I am writing a new answer here - I found this question while wondering the same thing and didn't find the existing answer that great).
A descriptor is defined on a class, but is typically called from an instance. When it's called from an instance both
instance
andowner
are set (and you can work outowner
frominstance
so it seems kinda pointless). But when called from a class, onlyowner
is set – which is why it's there.This is only needed for
__get__
because it's the only one that can be called on a class. If you set the class value you set the descriptor itself. Similarly for deletion. Which is why theowner
isn't needed there.Well, here's a cool trick using similar classes:
(I'm using Python 3; for python 2 you need to make sure those divisions are
/ 5.0
and/ 9.0
). That gives:Now there are other, arguably better ways to achieve the same effect in python (e.g. if celsius were a property, which is the same basic mechanism but places all the source inside the Temperature class), but that shows what can be done...
描述符是类命名空间中管理实例属性(如槽、属性或方法)的对象。例如:
迂腐地讲,描述符是具有以下特殊方法的对象,这些方法可能称为“描述符方法”:
__get__
:例如,非数据描述符方法在方法/函数上__set__
:数据描述符方法,例如在属性实例或槽上__delete__
:数据描述符方法,再次由属性或槽使用这些描述符对象是属性在其他对象类命名空间中。也就是说,它们位于类对象的
__dict__
中。描述符对象以编程方式管理普通表达式、赋值或删除中的点查找(例如
foo.descriptor
)的结果。函数/方法、绑定方法、属性、类方法和静态方法都使用这些特殊方法来控制如何通过点查找来访问它们。
数据描述符,例如
属性
,可以允许基于对象的更简单状态对属性进行惰性评估,从而允许实例使用比预先计算每个可能的属性更少的内存。另一个数据描述符,由
__slots__
创建的member_descriptor
,通过让类将数据存储在可变的类似元组的数据结构中,而不是更灵活但占用空间的__dict__
中,可以节省内存(并加快查找速度)。非数据描述符、实例和类方法从其非数据描述符方法
__get__ 获取其隐式第一个参数(通常分别命名为
- 这就是静态方法知道没有隐式第一个参数的方式。self
和cls
)大多数Python用户只需要学习描述符的高级用法,而无需进一步学习或理解描述符的实现。
但了解描述符的工作原理可以让人们对自己掌握 Python 更有信心。
深入:什么是描述符?
描述符是具有以下任何方法(
__get__
、__set__
或__delete__
)的对象,旨在通过点式查找用作如果它是一个实例的典型属性。对于具有descriptor
对象的所有者对象obj_instance
:obj_instance.descriptor
调用descriptor.__get__(self, obj_instance, Owner_class)
返回一个值
这就是属性上的所有方法和
get
的工作方式。obj_instance.descriptor = value
调用descriptor.__set__(self, obj_instance, value)
返回None
这就是属性上的
setter
的工作原理。del obj_instance.descriptor
调用descriptor.__delete__(self, obj_instance)
返回None
这就是属性上的
删除器
的工作原理。obj_instance
是其类包含描述符对象实例的实例。self
是描述符的实例(可能只是obj_instance
的类的一个实例)用代码来定义它,对象就是一个描述符如果其属性集与任何必需的属性相交:
A 数据描述符有一个
__set__
和/或__delete__
。非数据描述符既没有
__set__
也没有__delete__
。内置描述符对象示例:
classmethod
staticmethod
property
非数据描述
函数我们可以看到
classmethod
和 < code>staticmethod 是非数据描述符:两者都只有
__get__
方法:请注意,所有函数也是非数据描述符:
数据描述符、
property
然而,
property
是一个数据描述符:点式查找顺序
这些很重要区别,因为它们影响点式查找的查找顺序。
obj_instance
的__dict__,然后
这种查找顺序的结果是像函数/方法这样的非数据描述符可以被实例覆盖。
回顾和后续步骤
我们已经了解到,描述符是具有
__get__
、__set__
或__delete__
的对象。这些描述符对象可以用作其他对象类定义的属性。现在我们将使用您的代码作为示例来了解它们的使用方式。从问题中分析代码
这是您的代码,然后是您的问题和每个问题的答案:
您的描述符确保
Temperature
此类属性始终具有浮点数,并且您无法使用del
删除该属性:否则,您的描述符将忽略所有者 -相反,所有者的类和实例将状态存储在描述符中。您可以使用简单的类属性轻松地在所有实例之间共享状态(只要您始终将其设置为类的浮点数并且从不删除它,或者对代码的用户这样做感到满意)
:与您的示例的行为相同(请参阅下面对问题 3 的回复),但使用 Python 内置函数(
property
),并且会被认为更惯用:instance
是调用描述符的所有者的实例。所有者是描述符对象用于管理对数据点的访问的类。有关更多描述性变量名称,请参阅本答案第一段旁边定义描述符的特殊方法的描述。这是一个演示:
你不能删除该属性:
并且你不能分配一个不能转换为浮点数的变量:
否则,你在这里拥有的是所有实例的全局状态,它是通过分配给来管理的任何实例。
大多数经验丰富的 Python 程序员实现此结果的预期方法是使用属性装饰器,它在底层使用相同的描述符,但将行为引入所有者类的实现中(再次,如上面所定义):
它与原始代码段具有完全相同的预期行为:
结论
我们已经介绍了定义描述符的属性,数据描述符和非数据描述符之间的差异,使用它们的内置对象,以及有关使用的具体问题。
那么,您将如何使用问题的示例?我希望你不会。我希望您从我的第一个建议(一个简单的类属性)开始,如果您认为有必要,请继续讨论第二个建议(属性装饰器)。
Descriptors are objects in a class namespace that manage instance attributes (like slots, properties, or methods). For example:
Pedantically, descriptors are objects with any of the following special methods, which may be known as "descriptor methods":
__get__
: non-data descriptor method, for example on a method/function__set__
: data descriptor method, for example on a property instance or slot__delete__
: data descriptor method, again used by properties or slotsThese descriptor objects are attributes in other object class namespaces. That is, they live in the
__dict__
of the class object.Descriptor objects programmatically manage the results of a dotted lookup (e.g.
foo.descriptor
) in a normal expression, an assignment, or a deletion.Functions/methods, bound methods,
property
,classmethod
, andstaticmethod
all use these special methods to control how they are accessed via the dotted lookup.A data descriptor, like
property
, can allow for lazy evaluation of attributes based on a simpler state of the object, allowing instances to use less memory than if you precomputed each possible attribute.Another data descriptor, a
member_descriptor
created by__slots__
, allows memory savings (and faster lookups) by having the class store data in a mutable tuple-like datastructure instead of the more flexible but space-consuming__dict__
.Non-data descriptors, instance and class methods, get their implicit first arguments (usually named
self
andcls
, respectively) from their non-data descriptor method,__get__
- and this is how static methods know not to have an implicit first argument.Most users of Python need to learn only the high-level usage of descriptors, and have no need to learn or understand the implementation of descriptors further.
But understanding how descriptors work can give one greater confidence in one's mastery of Python.
In Depth: What Are Descriptors?
A descriptor is an object with any of the following methods (
__get__
,__set__
, or__delete__
), intended to be used via dotted-lookup as if it were a typical attribute of an instance. For an owner-object,obj_instance
, with adescriptor
object:obj_instance.descriptor
invokesdescriptor.__get__(self, obj_instance, owner_class)
returning avalue
This is how all methods and the
get
on a property work.obj_instance.descriptor = value
invokesdescriptor.__set__(self, obj_instance, value)
returningNone
This is how the
setter
on a property works.del obj_instance.descriptor
invokesdescriptor.__delete__(self, obj_instance)
returningNone
This is how the
deleter
on a property works.obj_instance
is the instance whose class contains the descriptor object's instance.self
is the instance of the descriptor (probably just one for the class of theobj_instance
)To define this with code, an object is a descriptor if the set of its attributes intersects with any of the required attributes:
A Data Descriptor has a
__set__
and/or__delete__
.A Non-Data-Descriptor has neither
__set__
nor__delete__
.Builtin Descriptor Object Examples:
classmethod
staticmethod
property
Non-Data Descriptors
We can see that
classmethod
andstaticmethod
are Non-Data-Descriptors:Both only have the
__get__
method:Note that all functions are also Non-Data-Descriptors:
Data Descriptor,
property
However,
property
is a Data-Descriptor:Dotted Lookup Order
These are important distinctions, as they affect the lookup order for a dotted lookup.
obj_instance
's__dict__
, thenThe consequence of this lookup order is that Non-Data-Descriptors like functions/methods can be overridden by instances.
Recap and Next Steps
We have learned that descriptors are objects with any of
__get__
,__set__
, or__delete__
. These descriptor objects can be used as attributes on other object class definitions. Now we will look at how they are used, using your code as an example.Analysis of Code from the Question
Here's your code, followed by your questions and answers to each:
Your descriptor ensures you always have a float for this class attribute of
Temperature
, and that you can't usedel
to delete the attribute:Otherwise, your descriptors ignore the owner-class and instances of the owner, instead, storing state in the descriptor. You could just as easily share state across all instances with a simple class attribute (so long as you always set it as a float to the class and never delete it, or are comfortable with users of your code doing so):
This gets you exactly the same behavior as your example (see response to question 3 below), but uses a Pythons builtin (
property
), and would be considered more idiomatic:instance
is the instance of the owner that is calling the descriptor. The owner is the class in which the descriptor object is used to manage access to the data point. See the descriptions of the special methods that define descriptors next to the first paragraph of this answer for more descriptive variable names.Here's a demonstration:
You can't delete the attribute:
And you can't assign a variable that can't be converted to a float:
Otherwise, what you have here is a global state for all instances, that is managed by assigning to any instance.
The expected way that most experienced Python programmers would accomplish this outcome would be to use the
property
decorator, which makes use of the same descriptors under the hood, but brings the behavior into the implementation of the owner class (again, as defined above):Which has the exact same expected behavior of the original piece of code:
Conclusion
We've covered the attributes that define descriptors, the difference between data- and non-data-descriptors, builtin objects that use them, and specific questions about use.
So again, how would you use the question's example? I hope you wouldn't. I hope you would start with my first suggestion (a simple class attribute) and move on to the second suggestion (the property decorator) if you feel it is necessary.
在深入了解描述符的详细信息之前,了解 Python 中的属性查找如何工作可能很重要。这假设该类没有元类,并且它使用 __getattribute__ 的默认实现(两者都可以用于“自定义”行为)。
在这种情况下,属性查找(在 Python 3.x 中或 Python 2.x 中的新式类)的最佳说明来自 了解 Python 元类(ionel 的代码日志)。该图像使用
:
代替“不可自定义的属性查找”。这表示在
Class
的实例
上查找属性foobar
:这里有两个条件很重要:
instance
的类有一个属性名称条目,它有__get__
和__set__
。实例
没有属性名称条目,但类有一个并且具有__get__
。这就是描述符的用武之地:
__get__
和__set__
。__get__
。在这两种情况下,返回值都会通过 __get__ 调用,以实例作为第一个参数,类作为第二个参数。
对于类属性查找,查找更加复杂(例如,参见 类属性查找(在上面提到的博客中))。
让我们转向您的具体问题:
在大多数情况下,您不需要编写描述符类!然而,您可能是一个非常普通的最终用户。例如函数。函数是描述符,这就是函数如何用作方法,并将
self
隐式传递为第一个参数。如果您在实例上查找
test_method
,您将得到一个“绑定方法”:类似地,您也可以通过手动调用其
__get__
方法来绑定函数(不推荐) ,仅用于说明目的):您甚至可以调用此“自绑定方法”:
请注意,我没有提供任何参数,并且该函数确实返回了我绑定的实例!
函数是非数据描述符!
数据描述符的一些内置示例是属性。忽略
getter
、setter
和deleter
,property
描述符是(来自 描述符操作指南“属性”):由于它是一个数据描述符,因此每当您查找 < 的“名称”时都会调用它code>property 并且它只是委托给用
@property
、@name.setter
和@name.deleter
修饰的函数(如果存在)。标准库中还有其他几个描述符,例如
staticmethod
、classmethod
。描述符的要点很简单(尽管您很少需要它们):用于属性访问的抽象公共代码。
property
是实例变量访问的抽象,function
提供方法的抽象,staticmethod
提供不需要实例访问的方法的抽象classmethod
为需要类访问而不是实例访问的方法提供了抽象(这有点简化)。另一个例子是类属性。
一个有趣的例子(使用 Python 3.6 中的
__set_name__
)也可以是只允许特定类型的属性:然后您可以在类中使用描述符:
并稍微使用它:
或者“懒惰” property”:
在这些情况下,将逻辑移动到公共描述符中可能是有意义的,但是也可以通过其他方式解决它们(但可能需要重复一些代码)。
这取决于您如何查找属性。如果您在实例上查找属性,则:
如果您在类上查找属性(假设描述符是在类):
所以基本上,如果您想在进行类级别查找时自定义行为,则第三个参数是必需的 - (因为
实例
是None
)。您的示例基本上是一个属性,它只允许可以转换为 float 的值,并且在该类的所有实例之间共享(以及在该类上 - 尽管只能在该类上使用“读取”访问权限)类,否则您将替换描述符实例):
这就是描述符通常使用第二个参数(
实例
)来存储值以避免共享它的原因。然而,在某些情况下,可能需要在实例之间共享值(尽管我目前无法想到场景)。然而,对于温度类别中的摄氏属性来说,它实际上没有任何意义……除非作为纯粹的学术练习。Before going into the details of descriptors it may be important to know how attribute lookup in Python works. This assumes that the class has no metaclass and that it uses the default implementation of
__getattribute__
(both can be used to "customize" the behavior).The best illustration of attribute lookup (in Python 3.x or for new-style classes in Python 2.x) in this case is from Understanding Python metaclasses (ionel's codelog). The image uses
:
as substitute for "non-customizable attribute lookup".This represents the lookup of an attribute
foobar
on aninstance
ofClass
:Two conditions are important here:
instance
has an entry for the attribute name and it has__get__
and__set__
.instance
has no entry for the attribute name but the class has one and it has__get__
.That's where descriptors come into it:
__get__
and__set__
.__get__
.In both cases the returned value goes through
__get__
called with the instance as first argument and the class as second argument.The lookup is even more complicated for class attribute lookup (see for example Class attribute lookup (in the above mentioned blog)).
Let's move to your specific questions:
In most cases you don't need to write descriptor classes! However you're probably a very regular end user. For example functions. Functions are descriptors, that's how functions can be used as methods with
self
implicitly passed as first argument.If you look up
test_method
on an instance you'll get back a "bound method":Similarly you could also bind a function by invoking its
__get__
method manually (not really recommended, just for illustrative purposes):You can even call this "self-bound method":
Note that I did not provide any arguments and the function did return the instance I had bound!
Functions are Non-data descriptors!
Some built-in examples of a data-descriptor would be
property
. Neglectinggetter
,setter
, anddeleter
theproperty
descriptor is (from Descriptor HowTo Guide "Properties"):Since it's a data descriptor it's invoked whenever you look up the "name" of the
property
and it simply delegates to the functions decorated with@property
,@name.setter
, and@name.deleter
(if present).There are several other descriptors in the standard library, for example
staticmethod
,classmethod
.The point of descriptors is easy (although you rarely need them): Abstract common code for attribute access.
property
is an abstraction for instance variable access,function
provides an abstraction for methods,staticmethod
provides an abstraction for methods that don't need instance access andclassmethod
provides an abstraction for methods that need class access rather than instance access (this is a bit simplified).Another example would be a class property.
One fun example (using
__set_name__
from Python 3.6) could also be a property that only allows a specific type:Then you can use the descriptor in a class:
And playing a bit with it:
Or a "lazy property":
These are cases where moving the logic into a common descriptor might make sense, however one could also solve them (but maybe with repeating some code) with other means.
It depends on how you look up the attribute. If you look up the attribute on an instance then:
In case you look up the attribute on the class (assuming the descriptor is defined on the class):
None
So basically the third argument is necessary if you want to customize the behavior when you do class-level look-up (because the
instance
isNone
).Your example is basically a property that only allows values that can be converted to
float
and that is shared between all instances of the class (and on the class - although one can only use "read" access on the class otherwise you would replace the descriptor instance):That's why descriptors generally use the second argument (
instance
) to store the value to avoid sharing it. However in some cases sharing a value between instances might be desired (although I cannot think of a scenario at this moment). However it makes practically no sense for a celsius property on a temperature class... except maybe as purely academic exercise.受到 Buciano Ramalho
Imaging 的 Fluent Python 的启发,你有一个像这样的类
我们应该验证重量和价格以避免为它们分配负数,如果我们使用描述符作为代理,我们可以编写更少的代码然后
像这样定义 LineItem 类:
我们可以扩展 Quantity 类来执行更常见的验证
Inspired by Fluent Python by Buciano Ramalho
Imaging you have a class like this
We should validate the weight and price in avoid to assign them a negative number, we can write less code if we use descriptor as a proxy as this
then define class LineItem like this:
and we can extend the Quantity class to do more common validating
您会看到 https://docs.python.org/3/howto/描述符.html#properties
You'd see https://docs.python.org/3/howto/descriptor.html#properties
易于理解(带有示例)
__get__ & 的解释__设置__ & __call__
在类中,什么是Owner、Instance
?在深入研究之前需要先了解一下:
__get__ __set__
被称为类的描述符工作/保存它们的内部属性,即:__name__
(类/所有者类的名称),变量-__dict__
等。稍后我将解释什么是所有者。1. 考虑这段代码:
因此,当调用
instance.method("first", "second")
时,会从 Method 类调用__call__
方法(call 方法使一个像函数一样可调用的类对象 - 每当类实例被调用时 __call__ 就会被实例化),并分配以下参数:instance: "first", arg1: "second"
,并且最后一个arg2被省略,这会打印出错误:TypeError: __call__()missing 1 requiredpositional argument: 'arg2'
2.如何解决它?
由于
__call__
采用instance
作为第一个参数(instance、arg1、arg2),但是instance
是什么?Instance
是调用描述符类(Method)的主类(MyClass)的实例。那么,instance = MyClass()
是实例
,那么谁是所有者
?持有描述符类的类 -MyClass
,但是,我们的描述符类(Method)
中没有方法将其识别为实例
。这就是我们需要 __get__ 方法的地方。再次考虑下面的代码:根据文档,暂时忘记 set:
__get__
“调用以获取所有者类的属性(类属性访问)或该类的实例(实例属性访问)。”如果您这样做:
instance.method.__get__(instance)
Prints:<__main__.MyClass object at 0x7fb7dd9eab90> <类'__main__.MyClass'>
这意味着实例:
MyClass
的对象,即instance
并且
Owner
是MyClass
本身3.
__set__
说明:__set__
用于设置类中的一些值>__dict__
对象(假设使用命令行)。设置 set 内部值的命令是:instance.descriptor = 'value'
# 在本例中,描述符是method
< em>(代码中的
instance.__dict__["method"] = value
只是更新描述符的__dict__
对象)所以这样做:
instance.method = 'value'
现在检查value = 'value'
是否在我们可以访问的
对象。__set__
方法中设置描述符方法
的__dict__做:
instance.method.__dict__
打印:{'_name': 'Internal call', 'value': 'value'}
或者你可以检查
使用 vars(instance.method) 的 __dict__ 值
prints:
{'name': '内部调用', 'value': 'value'}
我希望现在事情已经清楚了:)
Easy to digest (with example) Explanation for
__get__ & __set__ & __call__
in classes, what isOwner, Instance
?Some points to mug up before diving in:
__get__ __set__
are called descriptors of the class to work/save their internal attributes namely:__name__
(name of class/owner class), variables -__dict__
etc. I will explain what is an owner later1. Conside this code:
So, when
instance.method("first", "second")
is called,__call__
method is called from the Method class (call method makes a class object just callable like a function - whenever a class instance is called__call__
gets instiantiated), and following arguments are assigned:instance: "first", arg1: "second"
, and the last arg2 is left out, this prints out the error:TypeError: __call__() missing 1 required positional argument: 'arg2'
2. how to solve it?
Since
__call__
takesinstance
as first argument (instance, arg1, arg2), butinstance
of what?Instance
is the instance of main class (MyClass) which is calling the descriptor class (Method). So,instance = MyClass()
is theinstance
and so who is theowner
? the class holding the discriptor class -MyClass
, However, there is no method in our descriptor class(Method)
to recognise it as aninstance
. So that is where we need__get__
method. Again consider the code below:forget about set for now according to docs:
__get__
"Called to get the attribute of the owner class (class attribute access) or of an instance of that class (instance attribute access)."if you do:
instance.method.__get__(instance)
Prints:<__main__.MyClass object at 0x7fb7dd9eab90> <class '__main__.MyClass'>
this means instance: object of
MyClass
which isinstance
and
Owner
isMyClass
itself3.
__set__
Explaination:__set__
is used to set some value in the class__dict__
object (let's say using a command line). command for setting the internal value for set is:instance.descriptor = 'value'
# where descriptor ismethod
in this case(
instance.__dict__["method"] = value
in the code just update the__dict__
object of the descriptor)So do:
instance.method = 'value'
now to check if thevalue = 'value'
is set in the__set__
method we can access__dict__
object of the descriptormethod
.Do:
instance.method.__dict__
prints:{'_name': 'Internal call', 'value': 'value'}
Or you can check the
__dict__
value usingvars(instance.method)
prints:
{'name': 'Internal call', 'value': 'value'}
I hope things are clear now:)
我尝试了安德鲁·库克答案中的代码(按照建议进行了微小的更改)。 (我正在运行 python 2.7)。
代码:
结果:
对于 Python 3 之前的版本,请确保从对象进行子类化,这将使描述符正常工作,因为 get 魔法对旧样式类不起作用。
I tried (with minor changes as suggested) the code from Andrew Cooke's answer. (I am running python 2.7).
The code:
The result:
With Python prior to 3, make sure you subclass from object which will make the descriptor work correctly as the get magic does not work for old style classes.