Why can I not concatenate 'str' and 'instance' objects inside a class? - python

class myClass:
def __init__(self, text):
self.text = text
def printText(text):
more_text = "Why so "
return more_text + text
Above is a over-simplified version of a code that I am constructing to extract data out of webpages. I am running the temp.py code like this.
>>> from temp import myClass
>>> text = "serious?"
>>> joker_says = myClass(text)
>>>
>>> print joker_says.printText()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "temp.py", line 9, in printText
return more_text + text
TypeError: cannot concatenate 'str' and 'instance' objects
I have seen many examples of concatenation issues of 'str' and 'instance' objects in Stack Overflow.
I have tried the following ways:
OPTION 1: CONVERT text TO STRING DURING init AS INPUT
class myClass:
def __init__(self, str(text)):
self.text = text
def printText(text):
more_text = "Why so "
return more_text + text
But I get ...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "temp.py", line 3
def __init__(self, str(text)):
^
SyntaxError: invalid syntax
== == == == == ==
OPTION 2: CONVERT text TO STRING DURING init STEP
class myClass:
def __init__(self, text):
self.text = str(text)
def printText(text):
more_text = "Why so "
return more_text + text
But I get...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "temp.py", line 9, in printText
return more_text + text
TypeError: cannot concatenate 'str' and 'instance' objects
Can someone please give me a good solution to the problem? Please note that in my original code, my intention is to concatenate two string objects inside the class to create a webpage link. Any suggestion would be appreciated.

You have a couple issues here:
On every function you create for an object, self must be included as the first parameter.
Using your second example:
class myClass:
def __init__(self, text):
self.text = str(text)
def printText(self, text):
more_text = "Why so "
return more_text + text
Then, you make an instance of your class, and you can access the function printText :
joker = myClass("This is some text")
print(joker.text) # This prints: "This is some text"
print(joker.printText("serious?")) # This prints "Why so serious?"
If you want to use that same text as the initializing text, you need to reference it, instead of as a new parameter text, as an attribute of the class, as so:
class myClass:
def __init__(self, text):
self.text = str(text)
def printText(self):
more_text = "Why so "
return more_text + self.text
Then, if you want to reference the above:
joker = myClass("serious?")
print(joker.text) # This prints: "serious?"
print(joker.printText()) # This prints "Why so serious?"

The main problem you are facing is this:
def printText(text):
The reason why you are getting this error is because, as an instance method, the declaration expects you to have self (the instance object) passed as first argument. You are now passing text which is being used as self (the instance). This is why you get the error, because ultimately what you are actually doing is trying to add a string with an instance.
So, knowing that the first argument being passed implicitly to printText is the instance, and looking inside your method, you are actually wanting to reference the self.text inside your printText method. However, your instance being passed in to printText is actually referred to as text. This can be very confusing.
So, following the suggested nomenclature, you should be naming your instance argument as the "expected", self.
With this in mind, your text that you want to reference, can now be referenced as self.text.
This can be shown by fixing your code:
def printText(self):
more_text = "Why so "
return more_text + self.text

Related

TypeError: takes exactly 1 argument (2 given) TypeError: 'str' object is not callable

I am looking to brush up on my python so I am writing a small test class. I am making a boy class, and trying to create an instance of Boy called "shy boy" that will say what kind of boy he is (a shy boy) and "hello"
class Boy(object):
def __init__(self, name, shoe):
self.name = name
self.shoe = shoe
def speak(s):
return s
def shoeShize(shoe):
return "my shoe size is " + shoe
def myName(name):
return "hello i am a " + name
#class shyBoy(Boy)
shyBoy = Boy("shy boy", 9)
shyBoy.speak("hello")
print shyBoy.name("shy boy")
Right now I am getting these errors:
Traceback (most recent call last):
File "boy.py", line 17, in <module>
shyBoy.speak("hello")
TypeError: speak() takes exactly 1 argument (2 given)
➜ ~ python boy.py
Traceback (most recent call last):
File "boy.py", line 19, in <module>
print shyBoy.name("shy boy")
TypeError: 'str' object is not callable
Thanks!
You are missing the self arg in your function names:
class Boy(object):
def __init__(self, name, shoe):
self.name = name
self.shoe = shoe
def speak(self, s):
return s
def shoeShize(self, shoe):
return "my shoe size is " + shoe
def myName(self, name):
return "hello i am a " + name
If you don't specify self, Python will default to thinking whatever the first arg you've given it is self. Unless you decorate the method with #staticmethod, self will always be the default first argument, and it is implicitly passed in. Thus, in your method call, you are technically saying boy_inst.method(self, myarg). So if the function is only expecting self and you also give it name, then it will say "You've given me too many arguments." That takes care of the Argument number errors
For the string error you are seeing
boy.name is a class variable, not a function. Thus, saying boy.name('hi') is not setting the class variable, it is attempting to call a class method called name:
def name(self, arg):
return arg
You set name on instantiation of the class
shyboy = Boy("name", 9)
That will set the instance variables. To change these, you either need a method defined such as
# In my Boy class
def change_name(self, newname):
self.name = newname
Or you can create a new class
Speak needs to be passed self as a parameter, since you plan to call it from an instance of the class.
For your second issue, your function is named myName, while the property of the class is named name. If you want to print the associated name provided at initialization, just use print shyBoy.name. If you want to try to use the myName function, use print shyBoy.myName("shy boy"). Note the lack of parens for name, since it is not a function and thus takes no arguments.
(However, I'm a little confused as to why are you trying to pass name as an argument to myName if you already have saved it in your __init__?)

Calling class method within class error

class String(object):
def __init__(self, text):
self.text = text
def __repr__(self):
return "{}".format(self.text)
def reverse(self):
return self.text[::-1]
def isPalindrome(self):
return (self.reverse(self.text) == self.text)
def main():
string = String(input("Write a String: "))
if(string.isPalindrome()):
print("The string {{}} IS a Palindrome".format(string))
else:
print("The string {{}} is NOT Palindrome".format(string))
I have this classe that represents a String and i want to check is a object is a palindrome by calling the method isPalindrome. But when i call the string.isPalindrome i get this error:
Traceback (most recent call last):
File "palindrome.py", line 23, in <module>
main()
File "palindrome.py", line 17, in main
if(string.isPalindrome()):
File "palindrome.py", line 12, in isPalindrome
return (self.reverse(self.text) == self.text)
TypeError: reverse() takes 1 positional argument but 2 were given
The error comes from this line:
return (self.reverse(self.text) == self.text)
change it for this:
return (self.reverse() == self.text)
self can be really confusing, here is a really good article to understand how it works. Just so you understand, look at reverse() definition:
def reverse(self):
return self.text[::-1]
As you can see, self.text is already assigned. No need to pass it to the function when calling it.

Python operator overloading for join

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")]))

Python (OOP) list append error

in need of a little insight. I have the following python code:
>>> class invader:
... def __init__(self):
// list
... self.parameters = []
...
... def parameters(self):
... param = self.parameters
... param.append('3')
...
>>> invade = invader()
>>> invade.parameters()
Running this in terminal produces the following error message:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'list' object is not callable
How can I solve this?
You problem is using the same name for your attribute and method, rename self.parameters to self.param and use self.param in your method:
class invader:
def __init__(self):
self.param = []
def parameters(self):
self.param.append('3')
invade = invader()
invade.parameters()
print(invade.param)
In the last line:
invade.parameters()
You are effectively using the list parameters as a function. (Note () at the end)
Do a
print invade.parameters
will let you see the content of the list and remove the runtime error
Both your method and attribute contain the same name parameters so you can do as follows here:
def parameters(self):
self._parameters.append('3')
It's a common to encapsulate attributes with underscores, especially with methods of the same name.
Your method and attribute contain the same name parameters. Since data attributes will override method attributes with the same name. So invade.parameters is list, not function. You should rename your function, such as append_parameters.
If you want to call parameters function, you can try this way:invader.parameters(invade).But it's not recommended

exec to add a function into a class

So I've looked at similar questions, and I've found some solutions to this, but I can't quite figure out how to do this.
What I'm trying to do is add a method to a class from a string. I can do this with the setattr() method, but that won't let me use self as an attribute in the extra method. Here's an example: (and I apologize for the variable names, I always use yolo when I'm mocking up an idea)
class what:
def __init__(self):
s = 'def yolo(self):\n\tself.extra = "Hello"\n\tprint self.extra'
exec(s)
setattr(self,"yolo",yolo)
what().yolo()
returns this:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: yolo() takes exactly 1 argument (0 given)
and if s = 'def yolo():\n\tself.extra = "Hello"\n\tprint self.extra'
then I get this result:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 2, in yolo
NameError: global name 'self' is not defined
This essentially means that I cannot dynamically create methods for classes, which I know is bad practice and unpythonic, because the methods would be unable to access the variables that the rest of the class has access to.
I appreciate any help.
You have to bind your function to the class instance to turn it into a method. It can be done by wrapping it in types.MethodType:
import types
class what:
def __init__(self):
s = 'def yolo(self):\n\tself.extra = "Hello"\n\tprint self.extra'
exec(s)
self.yolo = types.MethodType(yolo, self)
what().yolo()
On a side note, why do you even need exec in this case? You can just as well write
import types
class what:
def __init__(self):
def yolo(self):
self.extra = "Hello"
print self.extra
self.yolo = types.MethodType(yolo, self)
what().yolo()
Edit: for the sake of completeness, one might prefer a solution through the descriptor protocol:
class what:
def __init__(self):
def yolo(self):
self.extra = "Hello"
print self.extra
self.yolo = yolo.__get__(self)
what().yolo()
Another way, seems more elegant to me:
class what:
pass
ld = {}
exec("""
def yolo(self):
self.extra = "Hello"
print(self.extra)
""", None, ld)
# print('locals got: {}'.format(ld))
for name, value in ld.items():
setattr(what, name, value)
what().yolo()

Categories