views:

165

answers:

3

Why I have problem creating a class the inherite from str (or also int)

class C(str):
   def __init__(self, a, b):
     str.__init__(self,a)
     self.b = b

C("a", "B")

TypeError: str() takes at most 1 argument (2 given)

tha same happens if I try to use int instead of str, but it works with custom classes. I need to use __new__ instead of __init__? why?

A: 

Use __new__ in case of immutable types:

class C(str):
    def __new__(cls, content, b):
        return str.__new__(cls, content)

    def __str__(self):
        return str.__str__(self)

a=C("hello", "world")
print a

print returns hello.

Python strings are immutable types. The function __new__ is called to create a new instance of object C. The python __new__ function is basically exists to allow inheritance from immutable types.

zoli2k
`type(C("a", "B"))` -> `NoneType`
wiso
do you try your answer? Now it return ''
wiso
-1? Can you at least write a comment?
zoli2k
I wrote 2 comments.
wiso
And have you checked the updates of the post? It was updated according your request. You down-voted after the changes were made. Moreover, in your comment you expected functionality from my code which was not requested in your question (to get __str__() function work for the inherited class)!
zoli2k
After the first implementation the code returned `NoneType`, after the second it gave `""`. Which not requested functionality? I didn't ask a `__str__` function and it is not necessary, because the class inherited from `str`, see gnibbler answer. Now I can't modify the downvote, I need that you edit your answer.
wiso
+1  A: 
>>> class C(str):
...     def __new__(cls,*args,**kw):
...         return str.__new__(cls,*args,**kw)
... 
>>> c=C("hello world")
>>> type(c)
<class '__main__.C'>

>>> c.__class__.__mro__
(<class '__main__.C'>, <type 'str'>, <type 'basestring'>, <type 'object'>)

Since __init__ is called after the object is constructed, it is too late to modify the value for immutable types. Note that __new__ is a classmethod, so I have called the first parameter cls

See here for more information

>>> class C(str):
...     def __new__(cls,value,meta):
...         obj = str.__new__(cls, value)
...         obj.meta = meta
...         return obj
... 
>>> c=C("hello world", "meta")
>>> c
'hello world'
>>> c.meta
'meta'
gnibbler
thanks, this is not the answer to my question, but this is the point. Now it works. In particular I have implemented `__init__` and changed the arguments of `__new__` to `(cls, a, b)`
wiso
@wiso, can you clarify the question? I am not sure what you are trying to achieve with `a` and `b`
gnibbler
this is only an example, I want to create a string with metainformation
wiso
@wiso, ok i added an example storing extra data in an attribute called meta. You could also use `*args` or `**kw` instead of meta if that suits your purposes better
gnibbler
+1  A: 

Inheriting built-in types is very seldom worth while. You have to deal with several issues and you don't really get much benefit.

It is almost always better to use composition. Instead of inheriting str, you would keep a str object as an attribute.

class EnhancedString(object):
     def __init__(self, *args, **kwargs):
         self.s = str(*args, **kwargs)

you can defer any methods you want to work on the underlying str self.s manually or automatically using __getattr__.

That being said, needing your own string type is something that should give you pause. There are many classes that should store a string as their main data, but you generally want to use str or unicode (the latter if you're representing text) for general representation of strings. (One common exception is if you have need to use a UI toolkit's string type.) If you want to add functionality to your strings, try if you can to use functions that operate on strings rather than new objects to serve as strings, which keeps your code simpler and more compatible with everyone else's programs.

Mike Graham
yes, this was my first solution, but the problem is that I have some functions that want a string argument (they are C++ functions from a library I can't modify), so with my implementation these functions work using my class as arguments. Can you better explain how to obtain this result using `__getattr__`?
wiso
@wiso, accepting a string as an argument is an underdefined thing. I don't know what your C++ library is like and how it's wrapped, but a wrapper should make it compatible with the right types for the job. Making a new class yourself that no one has ever seen before rather than using *its* string class or *Python's* string class (and it working with that) seems like a strange way to make things work.
Mike Graham
@wise, To use `__getattr__` to defer otherwise undefined attributes to a specific attribute in composition is accomplished by code like `def __getattr__(self, name): return getattr(self.s, name)`. Note that this code isn't necessarily recommended, but can work well in some cases; it's often better to manually define the things you want in composition.
Mike Graham
@Mike: I have `c_function(char*)`, I can use it in python as `c_function(s)` where `s` is a `str` instance. s represent something, and I want that it has a `name`, a `beautiful_name` and so on. So I have derived a class `C` from `str`, so I can pass an object c of class `C` to `python_function` that for example do `print c.name` and call `c_function(c)`
wiso