2

I'm trying to figure out how to change some value after each call of the object. I thougt that call() function is executed after each call.

This should be a simple counter class which decreases value attribute after being called.

class counter():
    def __init__(self,value):
        self.value = value

    def __call__(self):
        self.value -= 1

count = counter(50)
print count.value
print count.value

>> 50 
>> 50 <-- this should be 49

What am I doing wrong?

Milano
  • 18,048
  • 37
  • 153
  • 353
  • [`__call__`](https://docs.python.org/2/reference/datamodel.html#object.__call__) is called when you call your instance as a function. i.e after executing `count()`, `count.value` would be 49. You might be looking for [`__getattribute__`](https://docs.python.org/2/reference/datamodel.html#object.__getattribute__) – Matt Mar 24 '15 at 23:24
  • @Matt: I tried to use __getattribute__ but still the same. Can you help me? Thanks – Milano Mar 24 '15 at 23:31
  • Do you want count.value to decrease by 1 every time it is accessed? – Matt Mar 24 '15 at 23:32
  • @Matt Yes I want exactly that. – Milano Mar 24 '15 at 23:33
  • does it need to be a class or do you just need a counter? – Matt Mar 24 '15 at 23:35
  • @Matt I've started because I needed a counter but now, I'm very curious if something like that is possible. So I want it to be a class. – Milano Mar 24 '15 at 23:36

4 Answers4

3

__call__ is only invoked when you call the object using ()

To invoke this behaviour you'd have to do

class counter():
    def __init__(self,value):
        self.value = value

    def __call__(self):
        print 'called'
        self.value -= 1

count = counter(50)
print count.value
count()
print count.value

This may not be exactly what you wanted to do.

Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
3

If you're not committed to classes, you could use a function and abuse using mutable-types-as-default-initializers:

def counter(init=None, container=[0]):
    container[0] -= 1
    if init is not None: container[0] = init
    return container[0]


x = counter(100)
print(x) # 100
print( counter() )  # 99
print( counter() )  # 98
print( counter() )  # 97
# ...

Call counter with a single argument to set/initialize the counter. Since initialization is actually the first call to the function, it will return that number.

Call counter with no arguments to get the "next value".

(Very similar to what I suggested here)

Alternatively, for a syntax closer to what you had in your question, use properties:

class Counter(object):
    def __init__(self, init):
        self.val = init

    @property
    def value(self):
        val = self.val
        self.val -= 1
        return val

count = Counter(50)

print(count.value)  # 50
print(count.value)  # 49
print(count.value)  # 48
print(count.value)  # 47
#...

Here, you're creating a Counter object called count, then every time you call count.value it returns the current value and prepares itself for a future call by decrementing it's internal val attribute.

Again, the first time you request the value attribute, it returns the number you initialized it with.

If, for some reason, you want to "peek" at what the next call to count.value will be, without decrementing it, you can look at count.val instead.

Community
  • 1
  • 1
jedwards
  • 29,432
  • 3
  • 65
  • 92
  • You should a look into [closures](http://en.wikipedia.org/wiki/Closure_%28computer_programming%29). This is basically what you are doing by using `[]` as a default argument, but by using a closure instead, you can use your counter function to create multiple counters. Take a look at my [answer](http://stackoverflow.com/a/29244937/4687645) – Matt Mar 24 '15 at 23:52
2

Use the property decorator

class counter:
    def __init__(self, value):
        self._value = value + 1

    @property
    def value(self):
        self._value -= 1
        return self._value

count = Counter(50)
print(count.value)  # 50
print(count.value)  # 49

Alternatly, you could use a closure:

def Counter(n):
    n += 1
    def inner():
        n -= 1
        return n
    return inner

Though this has to be called every time you want to use it

count1 = Counter(50)
count2 = Counter(50)
print(count1())  # 50
print(count1())  # 49
print(count2())  # 50
print(count2())  # 49
print(count1())  # 48
Community
  • 1
  • 1
Matt
  • 724
  • 4
  • 11
0

Defining a custom call() method in the meta-class allows custom behaviour when the class is called, e.g. not always creating a new instance.As no new class instance is created call gets called instead of init.So do this to get the desired result

print count.value
count()
print count.value
avinash pandey
  • 1,321
  • 2
  • 11
  • 15