Python operator overloading for join - python

I have a class which instances shall be presented as string if being used in the context of a string. To be precise, a attribute pointing to a string should be returned.
The class looks as the following and works so far:
class Test:
string = ""
def __init__(self, value=""):
self.string = value
def __repr__(self):
return str(self.string)
def __str__(self):
return self.__repr__()
def __add__(self, other):
return "%s%s" % (self.string, other)
def __radd__(self, other):
return "%s%s" % (other, self.string)
Unfortunately, I get a TypeError when I try doing something like this:
>>> "-".join([Test("test"), Test("test2")])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sequence item 0: expected string, instance found
Doing the same with a string, I get the expected result:
>>> "-".join(["test", "test"])
'test-test'
>>>
I'm pretty sure I'm just missing the correct operator overload function.
Could somebody how to achieve this with class instances in the list?
Thanks.

join requires elements to be strings, as documented here:
https://docs.python.org/3/library/stdtypes.html#str.join
You need to explicitly convert your objects to strings, e.g.:
"-".join(map(str, [Test("test"), Test("test2")]))

Related

How to str.join() in list of objects

class A:
def __init__(self, text):
self.text = text
def __repr__(self):
return self.text
def __str__(self):
return self.text
def __add__(self, other):
return str(self) + other
def __radd__(self, other):
return other + str(self)
I want that list of A objects to be "joinable" with str.join().
Which special method should I implement to achieve this?
Of course, I can extract first a list of text then join it, but it's not what I want.
b = A('Hello')
c = A('World')
l = [b, c]
print b, c
print l
print b + ' ' + c
print ' '.join(l) # <- Error here
Hello World
[Hello, World]
Hello World
Traceback (most recent call last):
File "sandbox.py", line 24, in <module>
print ' '.join(l)
TypeError: sequence item 0: expected string, instance found
__str__ is only used if the user of your object wants to turn it into a string, and str.join doesn't. You have to explicitly convert your objects to str values before str.join will use them.
(Could str.join have been defined to implicitly call str on each element in the iterable it receives as an argument? Sure. But it wasn't.)
One of the tenets of Python is "Explicit is better than implicit". If you want to str.join to join the string representations of your objects, pass str objects to join.
print(' '.join(str(x) for x in l))
The only way join will treat them as str objects implicitly is if they are str objects, which is to say if A is a subclass of str.
class A(str):
def __new__(cls, text):
obj = super().__new__(cls, text)
# Do whatever else you need to add to obj
return obj
l = [A('hello'), A('world')]
print(' '.join(l))
Beware of using inheritance where it isn't warranted, though.

Adding objects as keys in Dictionary

I am using a object as key and number as value but getting the below error in line. Any help ?
dict[a] = 1
:
Traceback (most recent call last):
File "detect_hung_connections.py", line 24, in <module>
dict = {a:1}
TypeError: __hash__() takes exactly 3 arguments (1 given)
My Code is as follows:
class A:
def __init__(self,a,b):
self.a = a
self.b = b
def __hash__(self,a,b):
return hash(self.a,self.b)
def __eq__(self,other):
return (self.a,self.b) == (other.a,other.b)
dict ={}
a = A("aa","bb")
dict[a] = 1
b = A("aa","bb")
The signature of A.__hash__ should not take any extra arguments.
def __hash__(self):
return hash((self.a,self.b))
You're calling hash with the entire object and (redundantly) its two attributes. You can use only a single value to hash. Try this, perhaps:
def __hash__(self):
return hash(self.a + self.b)
This at least passes execution.

How to have a list in python which returns a default item on an invalid index or no index at all

I am trying to create a custom python shell where I initialize some objects on startup. Those objects could be individual items or list. the user of the shell could then call methods from those objects to perform some functions:
For Ex, this code is run on the start of the python shell:
# Init the objects on startup of shell
if NeedObjList:
a = [ObjClass(x,y,z) ObjClass(p,q,r)]
else:
a = ObjClass(x, y, z)
Now in the shell, user will have to know that the initialized variable 'a' is a list or a class object. There can be a message displayed on startup of the python shell. But in case it is not seen, user might try to access the variable as a list when it was an object and vice versa.
I was wondering if the list itself can be created as a smart list. i.e. if the user supplies an index to the list, the object at the index is returned. Otherwise in case of an invalid index or no index being given, the list returns the 0th element (or any fixed element for that matter)
An example of the use case:
class example:
def __init__(self):
self.a=1
self.b=2
def cl_print(self):
print "works : {0} {1}".format(self.a, self.b)
if NeedObjList:
a = [example() example()]
else:
a = example()
Now in the shell, this file is imported on start (using "python -i"):
Case 1: Object list was created
>>>a[0].cl_print()
works : 1 2
>>> a.cl_print() # will fail
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'cl_print'
Case 2: Object was created
>>> a.cl_print()
works : 1 2
>>> a[0].cl_print()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: hello instance has no attribute '__getitem__'
I wanted to try and see if I could make things work for user without them having to do a try-except.
Making __getitem__() return the instance:
class Example(object):
def __init__(self):
self.a=1
self.b=2
def cl_print(self):
print("works : {0} {1}".format(self.a, self.b))
def __getitem__(self, item):
return self
and delegating attribute lookup of not found attributes to
the first list member with __getattr__() would work:
class MyList(list):
def __getattr__(self, attr):
return getattr(self[0], attr)
With a list:
>>> a = MyList([Example(), Example()])
>>> a.cl_print()
works : 1 2
>>> a[0].cl_print()
works : 1 2
With an instance:
>>> a = Example()
>>> a.cl_print()
works : 1 2
>>> a[0].cl_print()
works : 1 2
You can implement __iter__, __len__, and __getitem__ on your ObjClass to make it act like a list of one item:
class ObjClass(object):
def __iter__(self):
yield self
def __len__(self):
return 1
def __getitem__(self, i):
if i == 0: return self
raise IndexError("list index out of range")
Use try ... except:
tokens=['aaaa', 'bbb', 'ccc', 'ddd', 'eee']
try:
a = tokens[N]
except IndexError:
a = tokens[0]
If N-th element exists in list, then it will be returned, otherwise 0 element (or some other) will be returned.

python: unexplainable infinite recursion with __repr__

Here's a piece of code, which goes into an infinite recursion loop, which consists only of __repr__ function, seemingly calling itself. But I really can't see, how it calls itself. Moreover, I can't even understand, how it was called:
class MyList(list): #this is storage for MyDict objects
def __init__(self):
super(MyList, self).__init__()
class MyDict(dict):
def __init__(self, mylist):
self.mylist = mylist #mydict remembers mylist, to which it belongs
def __hash__(self):
return id(self)
def __eq__(self, other):
return self is other
def __repr__(self):
return str(self.mylist.index(self)) #!!!this is the crazy repr, going into recursion
def __str__(self):
return str(self.__repr__())
mylist = MyList()
mydict = MyDict(mylist)
mydict.update({1:2})
print str(mylist.index(mydict)) #here we die :(
Execution of this code results in:
Traceback (most recent call last):
File "test_analogue.py", line 20, in <module>
print str(mylist.index(mydict))
File "test_analogue.py", line 13, in __repr__
return str(self.mylist.index(self))
File "test_analogue.py", line 13, in __repr__
...
... (repetition of last 2 lines for ~666 times)
...
File "test_analogue.py", line 13, in __repr__
return str(self.mylist.index(self))
RuntimeError: maximum recursion depth exceeded while calling a Python object
Do you understand, how str(mylist.index(mydict)) managed to call __repr__? I'm completely puzzled. Thanks!
>> mylist.index('foo')
ValueError: 'foo' is not in list
You never actually added mydict to mylist, so the index method tries to raise this error. The error contains the repr of the dict. The repr of the dict, of course, tries to look up its index in the list that it isn't in, and this raises an exception, whose error message is calculated using the repr of the dict, which of course, tries to look up its index in the list that it isn't in, and...

How can I detect whether my python class instance is cast as an integer or float?

I have a python class to calculate the number of bits when they have been specified using "Kb", "Mb" or "Gb" notation. I assigned a #property to the bits() method so it will always return a float (thus working well with int(BW('foo').bits)).
However, I am having trouble figuring out what to do when a pure class instance is cast as an int(), such as int(BW('foo')). I have already defined __repr__() to return a string, but it seems that that code is not touched when the class instance is cast to a type.
Is there any way to detect within my class that it is being cast as another type (and thus permit me to handle this case)?
>>> from Models.Network.Bandwidth import BW
>>> BW('98244.2Kb').bits
98244200.0
>>> int(BW('98244.2Kb').bits)
98244200
>>> BW('98244.2Kb')
98244200.0
>>> type(BW('98244.2Kb'))
<class 'Models.Network.Bandwidth.BW'>
>>>
>>> int(BW('98244.2Kb'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string or a number, not 'BW'
>>>
Read this
http://docs.python.org/reference/datamodel.html#emulating-numeric-types
__int__()
__float__()
Basically what you want lies within overriding __trunc__ and __float__ in the Models.Network.Bandwidth.BW class:
#!/usr/bin/python
class NumBucket:
def __init__(self, value):
self.value = float(value)
def __repr__(self):
return str(self.value)
def bits(self):
return float(self.value)
def __trunc__(self):
return int(self.value)
a = NumBucket(1092)
print a
print int(a)
print int(a.bits())

Categories