开发者

How to add a constructor to a subclassed numeric type?

I want to subclass a numeric type (say, int) in python and give it a shiny complex constructor. Something like this:

class NamedInteger(int):
    def __init__(self, value):
        super(NamedInteger, self).__init__(value)
        self.name = 'pony'

    def __str__(self):
        return self.name

x = NamedInteger(5)
print x + 3
print str(x)

This works fine under Python 2.4, but Python 2.6 gives a deprecation warning. What is the best way to subclass a numeric type and to redefine constructors for builtin types in newer Python versions?

Edit: Spotted in comments that this works without a super() line, so it could be like this:

class NamedInteger(int):
    def __init__(self, value):
        self.name = 'pony'

    def __str__(self):
        return self.name

x = NamedInteger(5)
print x + 3
print str(x)

I believe that this works because int is immutable type and has only __new__ method. However I would be glad to know a correct way of subclassing, so I could build a class with behaviour like this:

x = NamedInteger(5, 'kitty')

Second edit:

The final version now looks like this:

class NamedInteger(int):
    def __new__(cls, value, name='pony'):
        self = super(NamedInteger, cls).__new__(cls, value)
        self.name = name
        return self

    def __str__(self):
        return self.name

x = NamedInteger(5)
y = NamedInteger(3, 'kitty')
print "%d %d" % (x, y)
print "%s %s" 开发者_开发百科% (x, y)

Answers below also gave very interesting links to Abstract Base Classes and numbers modules.


You have to use __new__ instead of __init__ when you subclass immutable built-in types, e.g. :

class NamedInteger(int):

    def __new__(cls, value, name='pony'):
        inst = super(NamedInteger, cls).__new__(cls, value)
        inst.name = name
        return inst

    def __str__(self):
        return self.name

x = NamedInteger(5)
print x + 3                 # => 8   
print str(x)                # => pony
x = NamedInteger(3, "foo")  
print x + 3                 # => 6
print str(x)                # => foo


As of Python 2.6, the preferred way to extend numeric types is not to directly inherit from them, but rather to register your class as a subclass of the Number abstract base class. Check out the abc module for documentation of the Abstract Base Class concept.

That module's documentation links to the numbers module, which contains the abstract base classes you can choose to declare yourself part of. So basically you'd say

import numbers
numbers.Number.register(NamedInteger)

to indicate that your class is a type of number.

Of course, the problem with this is that it requires you to implement all the various handler methods such as __add__, __mul__, etc. However, you'd really have to do this anyway, since you can't rely on the int class' implementation of those operations to do the correct thing for your class. For example, what's supposed to happen when you add an integer to a named integer?

My understanding is that the ABC approach is intended to force you to confront those questions. In this case the simplest thing to do is probably to keep an int as an instance variable of your class; in other words while you will register your class to give it the is-a relationship with Number, your implementation gives it a has-a relationship with int.


It will work fine if you don't pass value to super(NamedInteger, self).__init__()

I wonder why though, I'm learning from your post :-)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜