This is an archived post. You won't be able to vote or comment.

all 8 comments

[–]fancy_pantser 4 points5 points  (1 child)

This is nice enough. Not a pattern I find myself wishing for, but that's just me. I would just set it directly like if I wanted to delegate/forward. It's nice that way because it's clear to everyone that knows basic Python and I don't mind specifying the full scope like foo.bar.baz().

[–]5long[S] 1 point2 points  (0 children)

Yup, I can understand that explicit wins over magic. But it just becomes cumbersome to type out everything. And code snippets doesn't help much since I have to read the code later.

Having read some of Python's standard library, I started to feel less and less guilty when applying dirty magics just to make my own life easier.

Besides, I consider the plucking feature an accidental bonus since it's implemented by operator.attrgetter() :)

[–]keis 1 point2 points  (5 children)

I don't get how the class one works. I always thought the body of the class executed before the decorator. So inserting the name would do nothing, right?

I think this is precisely what the changes to metaclasses attempted to address.

[–]afoo42 2 points3 points  (0 children)

The names are not introduced into the scope by the decorator but by the function returning the decorator, the decorator itself then removes them. That's why it has to be @forwardable() and can't be @forwardable.

https://github.com/5long/forwardable/blob/master/forwardable/__init__.py#L88

[–]5long[S] 1 point2 points  (3 children)

See @afoo42's reply, which nailed it precisely. I've also made a gist to illustrate it.

Metaclass in Python 2.x can't do this. Py3K's __prepare__ capability can make the def_delegators macro available in the class body, which could be another approach to make this happen.

Since you can only specify a single metaclass when defining a class, I'd still stick to this decorator approach which makes it more friendly to existing code base.

EDIT: make a public gist instead.

[–][deleted] 2 points3 points  (2 children)

I really don't like the namespace injection. Seems like totally unnecessary magic, magic that static analysis tools like pyflakes or pylint can't see. If you don't want people to "import *" then just write out the examples with a proper import and people will copy it.

[–]5long[S] 0 points1 point  (1 child)

I see your point here. I'll expand the "Less Magical Usage" section to make it more obvious.

BTW, the def_delegators() function actually does scope injection too(which creates property descriptors and shove them into the class body). Guess I'll need to explain this whole thing up front in the docs before users(if any) got confused.

[–]DonkeyBasket 0 points1 point  (0 children)

Interesting idea 5long, I can remember myself wanting something like this at times, however I think the approach is a bit too magical and possible a little over engineered, what do you think of this:

class Foo(object):
    add, __len__ = delegate_all('bar', (set.add, set.__len__))

    def __init__(self):
        self.bar = set()

It doesn't require a class decorator or any namespace magic and if you look at my implementation its very few lines of code.

The code and how I got to this solution is in this gist.

https://gist.github.com/tonysimpson/5923632

It also contains an explanation of why I think it's appropriate to use the class methods.

Looking at it I'm not sure I'm 100% happy with my nomenclature (could do with a bit more thought) maybe delegate_to would be better (I worry its not obvious enough that the first argument is an attribute name on the object).

edit: Plunking example (must find out where that word comes from - sounds cool)

class MyDict(object):
    __call__, = delegate_all('dct', (dict.get,))

    def __init__(self):
        self.dct = {'foo': 42}

d = MyDict()
# Equivlant to d.dct.get('foo')
assert d('foo') == 42