-1

Adding a method to metaclass works perfectly in the below example.

class Test(object):

    def __init__(self, x):
        self.x = x

    def double(self):
        return self.x*2

# method to add
def quadruple(self):
    return self.x*4

# creating metaclass
TypeTest = type('TypeTest', (Test,), {'triple': triple, 
                                      'quadruple': quadruple})

# prints 8
TypeTest(2).quadruple()

The below example doesn't work and I have no idea why. It simply doesn't recognise self in the parsed function and a TypeError occurs.

class Vehicle(object):
    def __init__(self, wheels, door=False):
        self.wheels = wheels
        self.door = door

# method to add
def check_load(self, x):
    if x > self.load:
        return "Load won't fit"
    else:
        return "Load will fit"

# creating metaclass
Truck = type('Truck', (Vehicle,), dict(wheels=4,door=True, load=100,
                                       check_load=check_load))

# TypeError: check_load() missing 1 required positional argument: 'x'
Truck.check_load(10)
David Ross
  • 1,087
  • 1
  • 14
  • 21

1 Answers1

5

First of all: You are not creating a metaclass, you are creating regular classes. type() is the (base) metaclass here, calling it creates a new class object (the same type of object that a class statement produces).

The first type() call is essentially equivalent to:

class TypeTest(Test)
    triple = triple
    quadruple = quadruple

and the second example is the same as:

class Truck(Vehicle)
    wheels = 4
    door = True
    load = 100
    check_load = check_load

You forgot to create an instance of your Truck class:

Truck.check_load(10)

This leaves the check_load() function with nothing to bind to, there is no self.

In your first example you did create an instance:

TypeTest(2).quadruple()

Notice the call, passing in 2.

Create an instance for self to be bound to:

Truck(4, True).check_load(10)

If you wanted your class to not need arguments to create an instance, you'll need to provide a different __init__ method too, one that overrides the Vehicle.__init__ method:

def init(self): pass

Truck = type('Truck', (Vehicle,), dict(
    wheels=4,door=True, load=100,
    check_load=check_load, __init__=init))

Now you can create the instance without arguments:

Truck().check_load(10)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343