Python descriptor vs property [duplicate]
Asked Answered
D

2

25

I'm confused as to when to use a property vs a descriptor. I read that a property is a specialized descriptor.

Can someone please post how this works?

Druid answered 11/10, 2012 at 18:21 Comment(0)
I
34

You should read the docs on what descriptors actually are. The Cliff's Notes version: descriptors are a low-level mechanism that lets you hook into an object's attributes being accessed. Properties are a high-level application of this; that is, properties are implemented using descriptors. Or, better yet, properties are descriptors that are already provided for you in the standard library.

If you need a simple way to return a computed value from an attribute read, or to call a function on an attribute write, use the @property decorator. The descriptor API is more flexible, but less convenient, and arguably "overkill" and non-idiomatic in this situation. It's useful for more advanced use cases, like implementing bound methods, or static and class methods; when you need to know, for example, if the attribute was accessed through the type object, or an instance of the type.

Ingressive answered 11/10, 2012 at 18:27 Comment(2)
It may be worth noting that python discourages the use of getters and setters except where necessary ... or it may not ..Alphard
@JoranBeasley It's not as much discouraged as completely unnecessary. Since Python lets you override attribute access, you can decide you need this in a subclass. If you need a computed property, it's appropriate to use a getter. In Java/C#, you need to decide up front whether you want to allow for overriding access to a specific attribute, and provide for this at the top of the class hierarchy. This is why it's common to use properties pervasively: it's done "just in case".Ingressive
P
16

You can read more about both from here. But here's a simple example from the same book that tries to explain the differnce solving what essentialy is the same problem. As you can see, the implementation using properties is much simpler.

There are several ways that we can tap into Python's internal mechanisms for getting and setting attribute values. The most accessible technique is to use the property function to define get, set and delete methods associated with an attribute name. The property function builds descriptors for you. A slightly less accessible, but more extensible and reusable technique is to define descriptor classes yourself. This allows you considerable flexibility. You do this by creating a class which defines get, set and delete methods, and you associate your descriptor class with an attribute name.

The property function gives us a handy way to implement a simple descriptor without defining a separate class. Rather than create a complete class definition, we can write getter and setter method functions, and then bind these functions to an attribute name.

Descriptor example:

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 Farenheit( object ):
    def __get__( self, instance, owner ):
        return instance.celsius * 9 / 5 + 32
    def __set__( self, instance, value ):
        instance.celsius= (float(value)-32) * 5 / 9

class Temperature( object ):
    celsius= Celsius()
    farenheit= Farenheit()
>>>
oven= Temperature()
>>>
oven.farenheit= 450
>>>
oven.celsius
232.22222222222223
>>>
oven.celsius= 175
>>>
oven.farenheit
347.0

Property example:

class Temperature( object ):
    def fget( self ):
        return self.celsius * 9 / 5 + 32
    def fset( self, value ):
        self.celsius= (float(value)-32) * 5 / 9
    farenheit= property( fget, fset )
    def cset( self, value ):
        self.cTemp= float(value)
    def cget( self ):
        return self.cTemp
    celsius= property( cget, cset, doc="Celsius temperature" )
>>>
oven= Temperature()
>>>
oven.farenheit= 450
>>>
oven.celsius
232.22222222222223
>>>
oven.celsius= 175
>>>
oven.farenheit
347.0
Preoccupancy answered 11/10, 2012 at 18:33 Comment(4)
I believe your descriptor implementation of celsius is not correct. You should have set the celsius on instance rather than self; If you create two Temperature objects they will share the same celsius value.Bluefield
I ran the Descriptor example code and it works fine. But I didn't understand how instance.celsius is accessable in Farenheit class. Thank you.Ingrain
@Ingrain because class Farenheit two magic method declare instance arg,the intance arg means class Temperature,interpreter help us put this arg in background, you can see source cpython code : pyobject_genericgetattrwithdict ,or this article tenthousandmeters.com/blog/…Zima
Maybe pedantic, but I have to point out it's spelled “Fahrenheit”.Fortify

© 2022 - 2024 — McMap. All rights reserved.