'Why is __new__() asking for 4 arguments when initializing an object from a class derived from an abstract base class that wants one argument?

I am using Python 2.7 (using 3 in my case is not possible). I have a base class with some implemented methods and some abstract methods. I am using abc to implement the abstract base class, and the class inherits from ABCMeta, as recommended in some Python2.7 documentation.

import rospy
from bayesian.bbn import *
from cr_project.srv import ObservePatch, ObservePatchResponse, ConsumeFlower, ConsumeFlowerResponse
from abc import ABCMeta, abstractmethod
import numpy as np


# Base class
class Bee(ABCMeta):
    def __init__(self, name):
        self.name = name
        self.collected_nectar = 0
        self.bbn = build_bbn(
            self.f_C, self.f_B, self.f_N,
            domains = {
                'C':[0,1,2]
            })
        rospy.init(self.name)


    # Desirability of a flower based on color
    # 0 = red, 1 = blue, 2 = yelloe
    @abstractmethod
    def f_C(self, C):
        pass

    # Desirability of a flower based on how many bees it has
    @abstractmethod
    def f_B(self, B):
        pass

    # probability of a flower containing nectar
    @abstractmethod
    def f_N(self, C, B, N):
        pass

    def calc_patch_probabilities(self, observation):
        rows = observation.rows
        cols = observation.columns
        result = numpy.array([bbn.query(C=f[0], B=f[1]) for f in zip(observation.colors, observation.bees)])
        return result.reshape(rows,cols).tolist()

    def select_flower(self, prob2d):
        row = 0
        col = 0
        max = 0
        for r in range(0, len(prob2d)):
            for c in range(0, len(prob2d[0])):
                if prob2d[r][c] > max:
                    max = prob2d[r][c]
                    row = r
                    col = c

        return row, col


    def start(self):
        observe_srv = rospy.ServiceProxy('ObservePatch', ObservePatch)
        consume_srv = rospy.ServiceProxy('ConsumeFlower', ConsumeFlower)
        while not rospy.is_shutdown():
            # Observe the flower patch
            observe_req = ObservePatch() # no request data needed
            observation = observe_srv(observe_req)

            # Get probabilities for all flowers and normalize
            prob2d = self.calc_patch_probabilities(observation)

            # consume flower with highest probability
            row, col = self.select_flower(prob2d)
            consume_req = ConsumeFlower()
            consume_req.row = row
            consume_req.col = col
            consume_rsp = consume_srv(consume_req)
            self.collected_nectar += consume_rsp.nectar_received
            rospy.info('%s consumed %d nectar from flower at [%d, %d] and has collected a total of %d nectar', self.name, rsp.nectar_received, row, col, self.collected_nectar)

            # wait a random number of seconds (maximum of 3)
            rospy.sleep(random.uniform(0,3))

I then have a subclass that inherits from the Bee base class, and implements the missing functions. I also want to have the __init__() constructor set the name that is passed into the base class. This subclass is in a separate file which also has a main function and is executable.


class StupidBee(Bee):
    def __init__(self):
        super(StupidBee, self).__init__('StupidBee')

    def f_C(self, C):
        return 1.0/3.0 # Doesn't care about color

    def f_B(self, B):
        return 1.0 # Doesn't care

    def f_N(self, C, B, N):
        return 1.0


if __name__ == "__main__":
    try:
        b = StupidBee()
        b.start()
    except rospy.ROSInterruptException:
        pass


However, when I try to run it, I get an error with StupidBee().

Traceback (most recent call last):
  File "./StupidBee.py", line 27, in <module>
    b = StupidBee()
TypeError: __new__() takes exactly 4 arguments (1 given)

I am completely at a loss on what I'm doing wrong, to be honest. I have not declared __new__() anywhere, and I have no idea what arguments it needs. What am I missing?

(Note: the code is still a work-in-progress, was not tested yet, and probably has other mistakes and issues)

EDIT: Here's a minimal reproducible example

#!/usr/bin/env python

from abc import ABCMeta, abstractmethod

class Base(ABCMeta):
    def __init__(self, name):
        self.name = nam

    @abstractmethod
    def abs_method(self):
        pass

    def print_name(self):
        print self.name

class Derived(Base):
    def __init__(self):
        super(Derived, self).__init__('Derived')

    def abs_method(self):
        print "This is a Derived method"


d = Derived()
d.abs_method()


Solution 1:[1]

As a few commenters have figured out, you've inherited from the wrong base class. If you inherit from abc.ABCMeta, then you're making your own metaclass. If you just want to make an abstract base class, you want to either inherit from abc.ABC, or to declare that abc.ABCMeta is your class's metaclass.

In Python 2, you'll want:

from abc import ABC

class Bee(ABC):
    ...

Or:

from abc import ABCMeta

class Bee:
    __metaclass__ = ABCMeta
    ...

In Python 3, the latter would be:

from abc import ABCMeta

class Bee(metaclass=ABCMeta):
   ...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Blckknght