why descriptors? (WAS: Make staticmethod objects callable?)

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Steven Bethard

    why descriptors? (WAS: Make staticmethod objects callable?)

    Steven Bethard wrote:[color=blue]
    > (For anyone else out there reading who doesn't already know this,
    > Steven D'Aprano's comments are easily explained by noting that the
    > __get__ method of staticmethod objects returns functions, and classes
    > always call the __get__ methods of descriptors when those descriptors
    > are class attributes:[/color]

    Steven D'Aprano wrote:[color=blue]
    > Why all the indirection to implement something which is, conceptually,
    > the same as an ordinary function?[/color]

    While I wasn't around when descriptors and new-style classes were
    introduced, my guess is that it's mainly because what you *usually* want
    when defining a function in a class is for that function to be an
    instance method. That is, the following code::

    class C(object):
    def foo(self):
    pass
    c = C()
    c.foo()

    should be much more common than::

    class C(object):
    def foo():
    pass
    C.foo()

    because the whole point of creating a class is to allow you to create
    instances. But if ``C.foo`` and ``c.foo`` are just regular functions,
    then how will ``c.foo()`` get the ``self`` argument? Certainly a normal
    ``foo()`` shouldn't be inserting a magical ``self`` argument. So *some*
    indirection has to happen when a function is used in a class.

    Python's solution to this problem is to introduce descriptors, which are
    the "something" that classes have to do. All classes invoke __get__
    whenever any of their attributes are accessed. With a normal function
    object, invoking __get__ turns it into an instance method:
    [color=blue][color=green][color=darkred]
    >>> class C(object):[/color][/color][/color]
    .... pass
    ....[color=blue][color=green][color=darkred]
    >>> def foo(self):[/color][/color][/color]
    .... pass
    ....[color=blue][color=green][color=darkred]
    >>> foo[/color][/color][/color]
    <function foo at 0x00E69530>[color=blue][color=green][color=darkred]
    >>> foo.__get__(C() , C)[/color][/color][/color]
    <bound method C.foo of <__main__.C object at 0x00E738F0>>[color=blue][color=green][color=darkred]
    >>> class C(object):[/color][/color][/color]
    .... def foo(self):
    .... pass
    ....[color=blue][color=green][color=darkred]
    >>> C().foo[/color][/color][/color]
    <bound method C.foo of <__main__.C object at 0x00E59C50>>

    As a result, if you want to have a callable as a class attribute and you
    don't want that callable to give you an instance method when you access
    it, you can't use a regular Python function. Personally, I think that's
    pretty reasonable since 99% of the time, I *do* want an instance method[1].

    STeVe

    [1] The other 1% of the time, I pretty much always want a classmethod.
    I'm still convinced that staticmethods are basically silly when I can
    just declare a module level function. ;)
Working...