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 :-)
精彩评论