Searching to build up my personal style of programming i want to be able to call a python class the same way i'd call a python function.
Here's what i mean:
consider this function:
def Factorial(n):
if n == 0:
return 1
else:
return n * Factorial(n - 1)
This is a function that outputs 24 when you call Factorial(4).
Now let's consider a class instead:
class Factorial:
def __call__(n):
if n == 0:
return 1
else:
return n * Factorial()(n - 1)
This code works the same way as the previous code except at call time where you instead write:
Factorial()(4) # which outputs 24
Now my question is how could you do this instead:
Factorial(4) # just that, and output 24, from THE object.
Thanks!
Use self.
class Factorial:
def __call__(self, n):
if n == 0:
return 1
else:
return n * self(n - 1)
f = Factorial()
f(3)
# 6
f(5)
# 120
From there you can easily add a starting value in your __new__ constructor.
class Factorial:
def __new__(cls, value=None):
instance = super().__new__(cls)
if value is None:
return instance
else:
return instance(value)
def __call__(self, n):
if n == 0:
return 1
else:
return n * self(n - 1)
Now you can
Factorial(5)
# 120
f = Factorial()
f(5)
# 120
Use a constructor (which is __init__ in Python) like so:
class Factorial:
def __init__(self, n):
// code here
You can override __new__ method:
class Factorial:
def __new__(cls, n):
if n == 0:
return 1
else:
return n * Factorial(n - 1)
Although the above solution works, I prefer a decorator-based solution which is less magical and is more intuitive and explicit:
def function_factory(cls):
return cls()
#function_factory
class Factorial:
def __call__(self, n):
if n == 0:
return 1
else:
return n * Factorial(n - 1)
A short & simple change in your existing code:
>>> class Factorial:
... def __call__(self,n):
... if n == 0:
... return 1
... else:
... return n * self.__call__(n - 1)
...
>>> Factorial = Factorial()
>>> Factorial(4)
24
>>>
Related
The title pretty much explains the problem. I don't know if there's a practical solution to this or if I'm being too picky over the behavior of my code.
This article was hinting in the right direction, but I never got any code to work.
https://medium.com/#adamshort/python-gems-5-silent-function-chaining-a6501b3ef07e
Here's an example of the functionality that I want:
class Calc:
def __init__(self, n=0):
self.n = n
def add(self, n):
self.n += n
return self
def might_return_false(self):
return False
def print(self):
print(self.n)
return self
w = Calc()
# The rest of the chain after might_return_false should be ignored
addedTwice = w.add(5).might_return_false().add(5).print()
w.print() # Should print 5
print(addedTwice) # Should print False
I think the article meant something more or less like below (but I prefer the other answer using exception, as it's more readable and better testable).
Create a helper class:
class Empty:
def __call__(self, *args, **kwargs):
return self
def __getattr__(self, *args, **kwargs):
return self
def print(self, *args, **kwargs):
return False
and
def might_return_false(self):
return Empty()
Exceptions are a great way to interrupt a chained operation:
class CalcError(Exception):
pass
class Calc:
def __init__(self, n: int = 0):
self.n = n
def add(self, n: int) -> 'Calc':
self.n += n
return self
def might_raise(self) -> 'Calc':
raise CalcError
def __str__(self) -> str:
return str(self.n)
w = Calc()
try:
w.add(5).might_raise().add(5)
addedTwice = True
except CalcError:
addedTwice = False
print(w) # prints 5
print(addedTwice) # prints False
You could also do chains like:
w = Calc()
num_added = 0
try:
w.add(5)
num_added += 1
w.add(5)
num_added += 1
w.might_raise()
w.add(5)
num_added += 1
w.add(5)
num_added += 1
except CalcError:
print(f"Stopped after {num_added} additions")
If you attempt to do this with return instead of raise, you need to check the status at each step of the chain so that you can switch off to some other branch of code (probably via an if block). Raising an exception has the very useful property of immediately interrupting execution, no matter where you are, and taking you straight to the nearest matching except.
I tried :
def fibonnaci(n):
total_call = 0
if n ==0 or n == 1:
return 1
else:
if n== 2 or n == 1:
total_call +=0
else:
total_call +=2
return fibonnaci(n - 1) + fibonnaci(n - 2), total_call
n = 8
print(fibonnaci(n))
but I got a error:
TypeError: can only concatenate tuple (not "int") to tuple
How to display the number of calls for fibonnaci?
In your return statement the result of both fibonnaci(n - 1) and fibonnaci(n - 2) could be a tuple (argument > 1), or a single integer (argument <= 1) thus + means concatenation when the first one is a tuple. But when n == 3 in return fibonacci(n - 1) + fibonacci(n - 2), total_call fibonacci(2) is a tuple ((2, total_call)), while fibonacci(1) is an integer (1). So you want to concatenate a tuple with with an integer, which is impossible.
Using Decorators
Using Function Attributes
Reference
Code
def call_counter(func):
" Does the call count for any function "
def helper(x):
helper.calls += 1
return func(x)
helper.calls = 0
return helper
#call_counter
def fib(n):
if n ==0 or n == 1:
return 1
return fib(n - 1) + fib(n - 2)
Usage
fib(5)
print(fib.calls)
fib(10)
print(fib.calls) # Keeps running total so will be from previous
# fib(5) plus current fib(10)
# To reset counter
fib.calls = 0
Using Class
Reference
Code
class countCalls(object):
"""Decorator that keeps track of the number of times a function is called.
::
>>> #countCalls
... def foo():
... return "spam"
...
>>> for _ in range(10)
... foo()
...
>>> foo.count()
10
>>> countCalls.counts()
{'foo': 10}
Found in the Pythod Decorator Library from http://wiki.python.org/moin web site.
"""
instances = {}
def __init__(self, func):
self.func = func
self.numcalls = 0
countCalls.instances[func] = self
def __call__(self, *args, **kwargs):
self.numcalls += 1
return self.func(*args, **kwargs)
def count(self):
"Return the number of times this function was called."
return countCalls.instances[self.func].numcalls
#staticmethod
def counts():
"Return a dict of {function: # of calls} for all registered functions."
return dict([(func.__name__, countCalls.instances[func].numcalls) for func in countCalls.instances])
#countCalls
def fib(n):
if n ==0 or n == 1:
return 1
return fib(n - 1) + fib(n - 2)
Example
print(fib(3)) # Output 3
print(fib.count()) # Output 5
Advantage
Allows obtaining counts of all registered functions (i.e. registered by using decorator)
#countCalls
def f(n):
pass # dummy function
#countCalls
def g(n):
pass # dummy function
for i in range(5):
f(i)
for i in range(10):
g(i)
print(countCalls.counts())
# Outputs: {'f': 5, 'g': 10}
def fib(n):
if n <= 1:
return n, 1
fib_one = fib(n - 1)
fib_two = fib(n - 2)
#Return the result and the number of function calls (+ 1 for the current call)
return fib_one[0] + fib_two[0], fib_one[1] + fib_two[1] + 1
if __name__ == '__main__':
number_of_function_calls = fib(4)[1]
Fib(4) should return 9, which it does
fib(4)
fib(3) fib(2)
fib(2) fib(1) fib(1) fib(0)
fib(1) fib(0)
The problem is "obvious", if you bother to trace the values you're using:
return fibonnaci(n - 1) + fibonnaci(n - 2), total_call
When n is 3, this tries to "add" fibonnaci(2), a tuple, and fibonnaci(1), the integer 1. This is not a legal operation. You need to regularize your return values. You can't magically return the value alone (not the count) when that's what you want; you have to explicitly program the difference: dismember the tuple and add the component values.
Start with your base case being
return 1, 0
Your recursion case needs to add the components. Implementation is left s an exercise for the student.
Here's another answer using a decorator. The advantage of using a decorator is that the base function fib does not need to change. That means the total_count code and multiple return values can be discarded from your original attempt -
#counter(model = fib_result)
def fib(n = 0):
if n < 2:
return n
else:
return fib(n - 1) + fib(n - 2)
Our decorator counter accepts a model so we can respond to the behavior of the decorated function. Our decorated fib will return a fib_result such as { result: ?, count: ? }. That means we also need to handle fib(?) + fib(?) which is why we also defined __add__ -
class fib_result:
def __init__(self, result, count = 0):
if isinstance(result, fib_result):
self.result = result.result
self.count = result.count + count
else:
self.result = result
self.count = count
def __add__(a, b):
return fib_result(a.result + b.result, a.count + b.count)
As you can see, this fib_result is specific to counting fib calls. A different model may be required for counting other recursive functions that return other types of results.
All that's left now is to define our generic counter decorator. Our decorator takes arguments and returns a new decorator, lambda f: ..., which simply captures the passed arguments, lambda *args: ..., and constructs a new model with the result of f(*args) and a base count of 1 -
def counter(model = tuple):
return lambda f: lambda *args: model(f(*args), 1)
Here's how the complete program works -
r = fib(4)
print(f"answer: {r.result}, recurs: {r.count}")
# answer: 3, recurs: 9
r = fib(10)
print(f"answer: {r.result}, recurs: {r.count}")
# answer: 55, recurs: 177
I'm trying to implement a decorator that memoizes an arbitrary function. It appears I've successfully accomplished that with the following code:
def memoize(func):
cache = {}
def wrapper(*args, **kwargs):
acc = ""
for arg in args:
acc += str(arg)
if acc in cache:
return cache[acc]
else:
cache[acc] = func(*args, **kwargs)
return cache[acc]
return wrapper
#memoize
def fib(n):
if n == 0 or n == 1:
return 1
else:
return fib(n - 1) + fib(n - 2)
Then fib(100) returns 573147844013817084101 fairly quickly. However, if I don't use the syntactic sugar:
def fib(n):
if n == 0 or n == 1:
return 1
else:
return fib(n - 1) + fib(n - 2)
memoized = memoize(fib)
print memoized(100)
The function hangs. Debugging it, it looks like the wrapper returned is unable to modify the cache. Can someone explain this behavior? As far as I know there shouldn't be a difference between using the sugar and not using the sugar.
Your recursive call is not memoized, because you used a new name, not the original fib function name. Each fib() iteration calls back to fib(), but that will call the original, undecorated function.
Assign the return value of the decorator call to fib instead:
def fib(n):
if n == 0 or n == 1:
return 1
else:
return fib(n - 1) + fib(n - 2)
fib = memoize(fib)
print fib(100)
Alternatively, if you must use memoize as the name of the decorator result, have fib() call memoized() for recursive calls:
def fib(n):
if n == 0 or n == 1:
return 1
else:
return memoized(n - 1) + memoizzed(n - 2)
memoized = memoized(fib)
print memoized(100)
Remember, the #decorator syntax assigns to the same name, not a new name. The following two are equivalent:
#memoize
def fib(n):
# ....
and
def fib(n):
# ....
fib = memoize(fib) # Same name!
except the name fib is never bound to the original function first.
I am trying to understand python oop. but it's not easy for me. thus i wrote
python OOP program (ex.2) for below procedural program (ex.1) but It's not working with below error.
ex.1
def factorial(n):
num = 1
while n >= 1:
num = num * n
n = n - 1
return num
f = factorial(3)
print f # 6
ex.2
class factorial:
def __init__(self):
self.num = 1
def fact(self,n):
while n>=1:
num = self.num * n
n = n-1
return num
f = factorial()
ne= factorial.fact(3)
print(ne)
error
Traceback (most recent call last):
File "F:/python test/oop test3.py", line 13, in ne= factorial.fact(3)
TypeError: fact() missing 1 required positional argument: 'n'
use the instance you created to call the method:
f = factorial() # creates instance of the factorial class
ne = f.fact(3)
Or to call using the class itself without assignment:
ne = factorial().fact(3) # < parens ()
print(ne)
You also have a mistake you should be using self.num or you will always get 1 as the answer, so:
class Factorial: # uppercase
def __init__(self):
self.num = 1
def fact(self, n):
while n >= 1:
self.num = self.num * n # change the attribute self.num
n -= 1 # same as n = n - 1
return self.num
If you don't return your method will return None but you will still increment self.num so if you don't want to return but want to see the value of self.num after you call the method you can access the attribute directly:
class Factorial:
def __init__(self):
self.num = 1
def fact(self, n):
while n >= 1:
self.num = self.num * n
n -= 1
ne = Factorial()
ne.fact(5) # will update self.num but won't return it this time
print(ne.num) # access the attribute to see it
There are three problems:
1) Logical error: num = self.num * n should be changed
to self.num = self.num * n, Here num is another variable which you are creating.
2) Logical error, But If 1st one is addressed it becomes Syntax error:
return num should be changed to return self.num
3) Syntax Error:
f = factorial()
ne= factorial.fact(3)
should be either changed to
ne = factorial().fact(3) or ne = f.fact(3)
I am trying to generate the following sequence:
011212201220200112 ... constructed as follows: first is 0,
then repeated the following action:
already written part is attributed to the right with replacement
0 to 1, 1 to 2, 2 to 0.
E.g.
0 -> 01 -> 0112 -> 01121220 -> ...
I am trying to find the 3 billion-th element of this sequence.
I realized that the sequence grows exponentially and hence derived that:
log(base2) (3 billion) ~ 32
So I just need to generate this sequence 32 times.
Here is what I tried in python:
import os
import sys
s=['0']
num_dict = {'0':'1' , '1':'2' , '2':'0'}
def mapper(b):
return num_dict[b]
def gen(s):
while True:
yield s
s.extend( map(mapper,s) )
a = gen(s)
for i in xrange(32):
a.next()
print a.next()[3000000000 - 1]
The problem is my RAM gets filled up before hitting the 3 billion mark.
Is there a better way to do this problem ?
EDIT: This program could crash your machine.Please try for xrange(25) for testing purposes
There are enough hints in the comments that you should be able to find the one-line solution. I think that it's more interesting to try to derive it with a more general tool, namely, implicit data structures. Here's a class for singleton lists.
class Singleton:
def __init__(self, x):
self.x = x
def __getitem__(self, i):
if not isinstance(i, int): raise TypeError(i)
elif not (0 <= i < len(self)): raise IndexError(i)
else: return self.x
def __len__(self): return 1
We can use this class like so.
>>> lst = Singleton(42)
>>> lst[0]
42
>>> len(lst)
1
Now we define a concatenation class and a mapper class, where the latter takes a function and implicitly applies it to each list element.
class Concatenation:
def __init__(self, lst1, lst2):
self.lst1 = lst1
self.lst2 = lst2
self.cachedlen = len(lst1) + len(lst2)
def __getitem__(self, i):
if not isinstance(i, int): raise TypeError(i)
elif not (0 <= i < len(self)): raise IndexError(i)
elif i < len(self.lst1): return self.lst1[i]
else: return self.lst2[i - len(self.lst1)]
def __len__(self): return self.cachedlen
class Mapper:
def __init__(self, f, lst):
self.f = f
self.lst = lst
def __getitem__(self, i): return self.f(self.lst[i])
def __len__(self): return len(self.lst)
Now let's rewrite your code to use these classes.
a = Singleton(0)
for i in range(32):
a = Concatenation(a, Mapper({0: 1, 1: 2, 2: 0}.get, a))
print(a[3000000000 - 1])
As an exercise: why do we need cachedlen?