0

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!

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
moctarjallo
  • 1,479
  • 1
  • 16
  • 33
  • Why? Calling the `Factorial` class should return an object with type `Factorial`. You *can* do this, using metaclasses to give the `Factorial` class a different `__call__`, but you really shouldn't. – Patrick Haugh Jul 20 '18 at 17:59
  • 1
    OP, I think you need to understand the purpose of a Class and the purpose of its inherent encapsulation. A Class is not the same as a function. That being said, to make use of the constructor of a class, you should define `__init__` so you can pass the appropriate information into it in order to process, and could then kick off the processing in that function. – Fallenreaper Jul 20 '18 at 18:17
  • Have a look at my [answer](https://stackoverflow.com/a/51448410/820410) . If it solves your purpose, please accept/up vote it :) – Pankaj Singhal Jul 21 '18 at 03:08

4 Answers4

0

Use a constructor (which is __init__ in Python) like so:

class Factorial: def __init__(self, n): // code here

Zach
  • 156
  • 8
0

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
Michael Ekoka
  • 19,050
  • 12
  • 78
  • 79
  • Very nice work. You made sure that the normal functionality of a class doesn't break, and added the functionality i asked for. Very nice! – moctarjallo Jul 21 '18 at 15:30
0

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)
mhsekhavat
  • 977
  • 13
  • 18
0

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
>>> 
Pankaj Singhal
  • 15,283
  • 9
  • 47
  • 86