frans
frans

Reputation: 9808

Nice way to call a method of a property

Imagine the following code (which is totally useless if taken alone):

# define a property with additional methods
class P(property):
    def __init__(self, name):
        property.__init__(self, 
            fget=lambda self: self._get(name),
            fset=lambda self, v: self._set(name, v))
        self._name = name

    def some_fn(self):
        print('name: ' + self._name)

# define a class with two 'enhanced' properties
class C:
    p1 = P('p1')
    p2 = P('p2')

    def __init__(self):
        self._values = {}

    def _get(self, name):
        return self._values[name]

    def _set(self, name, v):
        self._values[name] = v

c = C()

c.p1 = 5
c.p2 = c.p1

print(c.p1, c.p2)

I just create a class C with two properties which have an extra method some_fn().

The problem is now: you can't call some_fn() easily by just writing c.p1.some_fn() because you would evaluate c.p1 first, which results in some value which doesn't provide the method any more.

I've tried to find some workarounds / approaches for calling some_fn in the context of a certain property, not it's value but I'm not happy yet.

My goal is quite simple:

I collected some code to make clear, what I'm talking about:

# ==== What I want to do ==============
c.p1.some_fn()              #  <-- this is what I want to write but
                            #      it's invalid since it evaluates to
                            #      5.some_fn()

some_fn(c.p1)               #  <-- something like this looks OK, too but
                            #      it evalueates to some_fn(5) (useless)

# ==== These are options that came to mind but I'm not happy with ======

getattr(C, 'p1').some_fn()  #  <-- this works but it is ugly

some_fn("c.p1")             #  <-- this is possible, too but I can't 
                            #      check integrity statically (pylint/mypy)

c.p1.value = c.p2.value     #  <-- this is a valid approach but it 
c.p1.some_fn()              #      increases

some_fn(c.p1)  # (again)    #  <-- This can acutally work if you `inspect`
                            #      the call stack inside `C._get()` but 
                            #      it's black magic and incredibly slow

with some_fn():             #  <-- this can work when `some_fn` changes 
    c.p1                    #      some global state which get's evaluated
                            #      inside `C._get()`

Upvotes: 0

Views: 59

Answers (1)

user2357112
user2357112

Reputation: 282026

My goal is quite simple: I want to be able read/assign properties without boilerplate: c.p1 = c.p2

If that is the goal here, it sounds like you've misunderstood properties, because they already work like that.

class C(object):
    @property
    def p1(self):
        # get value
    @p1.setter
    def p1(self, val):
        # set value

    @property
    def p2(self):
        # get value
    @p2.setter
    def p2(self, val):
        # set value

Then if you have an object c = C(), you can do c.p1 = c.p2, and it'll just work. Sticking more methods onto a property object is the wrong way to go.


If you really want to stick methods onto properties, retrieve the property through the class:

C.p1.some_fn()

Upvotes: 1

Related Questions