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()
Related
I was wondering what the best way to implement the following design would be in Python:
class Executor:
def __init__(self):
self.val = 5
def action(self):
self.action(self.val)
#classmethod
def action(cls, val):
print(f"Val is: {val}")
I want to be able to access the method both as an instance method that uses the value the object was initialised with, and as a class method which uses a passed in variable. Here is an example of the ways in which I would like to call it:
>>> Executor.action(3)
Val is: 3
>>> Executor().action()
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: action() missing 1 required positional argument: 'val'
I was thinking about trying to use keyword arguments, but I can't seem to get that to work either. Here is my code so far:
class Executor:
def __init__(self):
self.val = 5
#classmethod
def action(cls, val=None):
if val is None:
# This doesn't work; cls does not have attribute 'val'.
if hasattr(cls, "val"):
print(f"Val from instance: {cls.val}")
else:
raise ValueError("Called as class method and val not passed in.")
else:
print(f"Val passed in: {val}")
>>> Executor.action(3)
Val passed in: 3
>>> Executor().action()
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 13, in action
ValueError: Called as class method and val not passed in.
But the class instance does not have the val available for access.
The only other thing I can think of is using Hungarian notation, but it's not ideal as it's a bit messy, and it means there's multiple method names.
class Executor:
def __init__(self):
self.val = 5
def action_instance(self):
self.action_class(self.val)
#classmethod
def action_class(cls, val):
print(f"Val is: {val}")
>>> Executor.action_class(3)
Val is: 3
>>> Executor().action_instance()
Val is: 5
Any advice on a solid, clean approach would be greatly appreciated!
Cheers.
What you want to do looks strange to me, I am not sure you need that. Python cannot overload by method signatures/type, although there is the functools.singledispatch. So by defining action a second time you are actually replacing the first definition for the method.
The observable behaviour can be achieved with:
class Executor:
#classmethod
def action(cls, val=5):
return val
print(Executor().action())
print(Executor.action(3))
Outputs:
5
3
But again check first that you really need something like that, because it breaks one of the expectations of Python coders and Python data model: calling a method through the class is equivalent to calling the method through the instance given that you pass the instance to the class method.
obj = Executor()
obj.action() # this is the same as Executor.action(obj)
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
i'm doing an UNO game using Pygame, i'm doing it with lists of cards and classes of each color, when I tried to test the inheritance for using the color class, I got this error:
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
x=example()
File "C:/Users/Tamara/Desktop/TEC/UNO/UNOclases.py", line 93, in __init__
Red.__init__(self,Redlist)
TypeError: unbound method __init__() must be called with Red instance as first argument (got example instance instead)
Here's the code (don't mind if one of the names is wrong written, I had to translate it from spanish):
class Red:
def setSpecialRedCards(self):
self.__Redlist.append(self.__steal2)
self.__Redlist.append(self.__steal2)
self.__Redlist.append(self.__Reverse)
self.__Redlist.append(self.__Reverse)
self.__Redlist.append(self.__Jump)
self.__Redlist.append(self.__Jump)
def setRedNumers (self, number,counter):
while counter<=9:
if numero!=0:
self.__Redlist.append(number)
self.__Redlist.append(number)
else:
self.__listaRoja.append(number)
number+=1
counter+=1
def getRed(self):
return self.__Redlist
def __init__(self, Redlist=[]):
self.__Redlist=Redlist
self.__number0 = "red 0"
self.__steal2 = "steal2"
self.__Reverse = "Reverse"
self.__jump = "jump"
class example:
def __init__(self, Redlist=[]):
Red.__init__(self,Redlist)
def example2(self):
return Red.__number0
Thank you for your help!
Your class example doesn't inherit from class Red.
Write
class example(Red):
....
I'm a newbie and I'm having a really strange (like in the title, for me) problem.
This is the code:
from random import shuffle
class Carta:
listaSemi=[" ","Bastoni","Spade","Coppe","Denari"]
listaValori=[" ","Asso","Due","Tre","Quattro",
"Cinque","Sei","Sette","Otto","Nove","Dieci"]
def __init__(self,seme,valore):
self.seme=seme
self.valore=valore
def __str__(self):
s1=self.listaValori[self.valore]
s2=self.listaSemi[self.seme]
return " ".join((s1,"di",s2))
class Mazzo:
def __init__(self):
self.Carte=[]
def Crea(self):
for seme in range(1,5):
for valore in range(1,11):
self.Carte.append(Carta(seme, valore))
def Mescola(self):
shuffle(self.Carte)
When i do:
M=Mazzo
Mazzo.Crea(M)
I get:
Traceback (most recent call last):
File "<pyshell#62>", line 1, in <module>
Mazzo.Crea(M)
File "/home/administrator/Scrivania/Script/Carte.py", line 19, in Crea
self.Carte.append(Carta(seme, valore))
AttributeError: type object 'Mazzo' has no attribute 'Carte'
Thanks in advance for your help!
You are initilizing your class incorrectly. Try doing this:
m = Mazzo()
m.Crea()
So a quick explanation as to why. First of all, the first line initializes an object of type Mazzo and sets in to m (please note the '()' you need that for all methods, initialization or otherwise.) Since its only perimeter is self it can be left blank.
Next we want to call the Crea function, we do this by calling the object we just created and NOT the class itself.
You are assigning type to M instead of creating a new object. Probably you want to do this:
m=Mazzo()
m.Crea()
The problem is in how you are instantiating the class Mazzo
>>>M=Mazo
What is really assigned to M is the class object and not a Mazzo object:
>>>type(M)
<class 'type'>
Your next line is even more problematic:
>>> Mazzo.Crea(M)
That asks the Mazzo class to execute the Crea method, with a Mazzo class object as the argument. And Crea doesn't take any other arguments, let alone a class object. I suspect you may have some more reading to do about the self method argument in python.
The usual way of instantiating a new object of a given class is something like:
>>> class MyClass(object):
def __init__(self,someValue):
self.someValue = someValue
def aMethod(self)
print(self.someValue)
>>> newObject = MyClass(42)
>>> newObject.aMethod()
42
Good luck.
When you did
M=Mazzo
Mazzo.Crea(M)
What you did is Mazzo.Crea(Mazzo).
First off, M=Mazzo should be M=Mazzo(). When you call just plain Mazzo, it doesn't trigger __init__, although Mazzo() does.
FYI: calling Mazzo.Crea(Mazzo) will raise a TypeError, as in
TypeError: Crea takes exactly 1 argument (2 Given)
class Car:
# constructor
def __init__(self, make, model, year, mpg):
# instance variables
self.carMake = make
self.carModel=model
self.carYear = year
self.efficiency=mpg
self.gas = 0
# special method
def __str__(self):
return "%s %s %s"%(self.carYear, self.carMake, self.carModel)
def refuel(self,gallon):
if gallon < 0:
print("Sorry, amount cannot be negative")
else:
self.gas=self.gas+gallon
print (self.gas)
print("Added %.2f gallon of gas to the tank"%(self.gas))
def gas(self):
print(self.gas)
> Traceback (most recent call last): File "<pyshell#12>", line 1, in
> <module>
> c1.gas() TypeError: 'int' object is not callable
Your method gas and your instance attribute gas created in __init__ have the same name. The method is stored on the class, but is "shadowed" by the attribute stored on the instance, since Python first looks for names on the instance, then on the class and its parents.
So self.gas is an integer and you can't call it.
You have self.gas initialized to an int in the __init__() method, but then you define a method named gas() as well. Once __init__() runs, self.gas is an int. I'm guessing somewhere you are calling gas() on an instance of this class.
Rename your gas() method to something like print_gas(), or, wherever you're calling this, instead of doing c1.gas(), just do print c1.gas.
Consider this class Test in a file called test.py:
class Test:
def __init__(self):
self.x=3
def x(self):
print self.x
Now I import class Test in my console and see what methods it has:
>>> from test import Test
>>> [method for method in dir(Test) if callable(getattr(Test, method))]
['__init__', 'x']
Notice that it has the method x. Now let's create an instance of Test
>>> k=Test()
Let's see what methods we have
>>> [method for method in dir(k) if callable(getattr(k, method))]
['__init__']
>>>
As you can see the method x is no longer available. why?
When you created k as an instance of Test, it executes the __init__ method and sees self.x=3 which redefines x to be just a variable in self and your method x() is gone. So when you do k.x() it thinks that you are doing it on self.x that you set in __init__ which is not callable. However just k.x will work as I show below:
>>> k.x()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
>>> k.x
3
>>>
The conclusion is don't name your variables and methods the same.