-2

Two notable ways to create a class are shown below:

class Klass:
    pass

Klass = type("Klass", tuple(), dict())

I would like to override the constructor (__call__) while still using the class keyword instead of doing something else, like directly calling type. I really do want to override (__call__), not __init__

My failed attempts are shown below:

Attempt 1

class Foo:
    @classmethod
    def __call__(*args):
        print("arr har")
        return super(type(args[0]), args[0]).__call__(*args)

instance = Foo()
# did not print "arr har"

Attempt 2

class BarMeta(type):
    def __call__(*args):
        print("hello world")
        return super(type(args[0]), args[0]).__call__(*args[1:])
  • Attempt 2A

    class Bar:
        __metaclass__ = BarMeta
    
    instance = Bar()
    # did not print "hello world" 
    
  • Attempt 2B

    Baz = BarMeta("Baz", tuple(), dict())
    instance = Baz()
    # Did print "hello world," but we weren't able to use the `class` keyword to create `Baz`
    
Toothpick Anemone
  • 4,290
  • 2
  • 20
  • 42
  • But `__call__` is not he constructor, `__new__` is. If you want to override `__call__` all you have to do is override `__call__` – DeepSpace Oct 27 '19 at 12:06
  • Attempt 2A would work if you were using python 2. In python 3, the syntax for defining a metaclass is `class Bar(metaclass=BarMeta):`. – Aran-Fey Oct 27 '19 at 12:08
  • @DeepSpace But `__call__` calls `__new__`, not the other way around. `instance = Klass()` is the same as `instance = Klass.__call__()` or `instance = type(Klass).__call__(Klass)` – Toothpick Anemone Oct 27 '19 at 12:09
  • Also, I (and apparently at least 2 other people) had a hard time understanding your question. It would've been a good idea to start by explicitly stating what you want to do, i.e. "I want `Bar()` to print 'arr har'". – Aran-Fey Oct 27 '19 at 12:10
  • @SamuelMuldoon `__call__` does not call `__new__`. The only thing `__call__` does is make **instances** of the class callable. That's all. – DeepSpace Oct 27 '19 at 12:13
  • @DeepSpace `type.__call__` does indeed call `__new__` followed by `__init__`. That's why we can create instances of a class by calling that class. – Aran-Fey Oct 27 '19 at 12:14
  • @Aran-Fey do you have an example of `__call__` calling `__new__`? I don't seem to be able to reproduce such a case – DeepSpace Oct 27 '19 at 12:17
  • 1
    @DeepSpace `type.__call__` is really the only example. https://repl.it/repls/EvenSandyPress – Aran-Fey Oct 27 '19 at 13:03

1 Answers1

0

All credit does to Aran-Fey, who posted the answer as a comment, instead of as an answer:

class BarMeta(type):
    def __call__(*args):
        print("hello world")
        return super(type(args[0]), args[0]).__call__(*args[1:])    

class Bar(metaclass=BarMeta):
    pass

instance = Bar()
Toothpick Anemone
  • 4,290
  • 2
  • 20
  • 42