This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Add new attribute decorator (akin to property)?
Type: enhancement Stage: resolved
Components: Versions: Python 3.6
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: abarry, eric.snow, ethan.furman, r.david.murray, rhettinger, skip.montanaro
Priority: normal Keywords:

Created on 2015-08-20 01:16 by abarry, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (9)
msg248869 - (view) Author: Anilyka Barry (abarry) * (Python triager) Date: 2015-08-20 01:16
This is an issue that came up quite often when creating code where you want the class' namespace to hold the instance attributes. I've often seen (and written) code like this:

class Foo:
  def __init__(self):
    self._x = 42
  @property
  def x(self):
    return self._x

As an attempt to populate the class namespace with what should normally be available on the instance. In all my projects now I use my own custom decorator to get around that.

class attribute:
  def __init__(self, func):
    self.func = func
  def __get__(self, instance, owner):
    if instance is None:
      return self
    return self.func.__get__(instance, owner)

This permits instances to override attributes set as such, like this:

class Bar:
  def __init__(self):
    self.x = 42
  @attribute
  def x(self):
    pass # placeholder

I figured I might as well suggest the idea. I'm not attached to the name, and it's more for completion's sake rather than hard necessity.
msg248870 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2015-08-20 01:31
Could you give an actual use-case demo, and how it's different from @property?  Could just be that I'm tired, but I'm not seeing the advantages of @attribute.
msg248871 - (view) Author: Anilyka Barry (abarry) * (Python triager) Date: 2015-08-20 01:41
The only significant difference is that it lets the instance overwrite the attribute (it doesn't have __set__ or __delete__). For example (using fractions.Fraction to demonstrate), the following:

    def __new__(cls, numerator=0, denominator=None, _normalize=True):
        # ...
        self._numerator = numerator
        self._denominator = denominator

    @property
    def numerator(a):
        return a._numerator

    @property
    def denominator(a):
        return a._denominator

would become:

    def __new__(cls, numerator=0, denominator=None, _normalize=True):
        # ...
        self.numerator = numerator
        self.denominator = denominator

    @attribute
    def numerator(a):
        pass

    @attribute
    def denominator(a):
        pass

This is more of an aesthetic enhancement rather than a functional one.
msg248883 - (view) Author: Skip Montanaro (skip.montanaro) * (Python triager) Date: 2015-08-20 13:51
I'm missing the point too, I think. I don't see class attributes in your initial Foo class, then your second one as a __new__ method, but references self. I'm quite confused at this point.
msg248885 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2015-08-20 14:28
So you are basically wanting to be able to create read only attributes that don't have a _ in front of them even internally.

I don't think that's something Python core should add.  Read only attributes should be an exceptional case in most python programs.  Fraction uses them because Fraction is an immutable type and those properties are involved in the hash calculation.

I've written an immutable class with a number of attributes (email.policy), and my solution involved a custom __setattr__, which really makes more sense for my use case.  So I think this should be left to the application/library to decide. That is, the solution isn't universal enough to go in the stdlib, IMO.

So, long story short, I'm -1 :)
msg248908 - (view) Author: Eric Snow (eric.snow) * (Python committer) Date: 2015-08-20 18:08
This sort of thread belongs on python-ideas@python.org, not on the tracker.  Please post there (feel free to reference this issue; also add a link to the thread here).  TBH, I think there's a larger discussion to be had regarding the topic of other useful descriptors that we should consider adding to Python.

In the meantime I'm closing this issue.  We can re-open it if the outcome of any python-ideas discussions warrants it.
msg248910 - (view) Author: Eric Snow (eric.snow) * (Python committer) Date: 2015-08-20 18:09
That said...

What's the benefit of it being a decorator?  The docstring?  Access to func.__name__?  It could just as well be:

    class attribute:
        _name = None
        def __get__(self, instance, owner):
            if instance is None:
                return self
            if self._name is None:
                for name, attr in vars(owner).items():
                    if attr is self:
                        self._name = name
                        break
            return instance.__dict__[self._name]

However, note that this is a non-data descriptor since it lacks __set__ and __delete__.  That means it is *not* a read-only wrapper like property.  If that's not a concern then there's no point to using a descriptor at all since you can just use a constant as a place-holder:

    class Bar:
        x = ...
        def __init__(self):
            self.x = 42

If you *are* looking for a read-only wrapping descriptor then you'll need __set__ and __delete__ methods, likely ones that simply raise AttributeError.

FWIW, there are definitely plenty of useful things you can do with descriptors. [1]  Some could be useful enough to make it into the stdlib (or builtins), but I'm not convinced the one you are proposing has enough justification at this point.

[1] https://bitbucket.org/ericsnowcurrently/presentations/src/default/utpy-may2015/
msg248911 - (view) Author: Anilyka Barry (abarry) * (Python triager) Date: 2015-08-20 18:12
I figured. I guess it makes more sense to do that on a per-library basis. Eric does have a valid point, and perhaps this addition is not needed :) I realize now that this isn't really needed, backing off and closing :)
msg248912 - (view) Author: Eric Snow (eric.snow) * (Python committer) Date: 2015-08-20 19:21
No worries, Emanuel.  Thanks for bringing it up.  I'd still be interested to see what sort discussion ensued if you took this to python-ideas.  Starting a thread on the topic has been on my todo list for a while but other matters always end up taking precedence. :)
History
Date User Action Args
2022-04-11 14:58:19adminsetgithub: 69085
2015-08-20 19:21:41eric.snowsetmessages: + msg248912
2015-08-20 18:12:20abarrysetmessages: + msg248911
2015-08-20 18:09:25eric.snowsetmessages: + msg248910
2015-08-20 18:08:42eric.snowsetstatus: open -> closed

type: enhancement

nosy: + eric.snow, rhettinger
messages: + msg248908
resolution: rejected
stage: resolved
2015-08-20 14:28:40r.david.murraysetnosy: + r.david.murray
messages: + msg248885
2015-08-20 13:51:31skip.montanarosetnosy: + skip.montanaro
messages: + msg248883
2015-08-20 01:41:59abarrysetmessages: + msg248871
2015-08-20 01:31:13ethan.furmansetnosy: + ethan.furman
messages: + msg248870
2015-08-20 01:16:09abarrycreate