I am trying to define static variables in this example code:
class test_object:
resolution_default_opts = {'e':{'ylims':[0,2],'xlabel':'resolution'},'t':{'ylog':True,'xlabel':'resolution'}}
ee_default_opts = {'eee':{'xlabel':'parameter number'}}
default_opts = {'resolution':test_object.resolution_default_opts,'ee':test_object.ee_default_opts}
def __init__(self,name):
self.name = name
def foo(self,input):
return default_opts[input]
an_instance = test_object('a_name')
print(an_instance.foo('ee'))
However when running the above I get the following error :
default_opts = {'resolution':test_object.resolution_default_opts,'ee':test_object.ee_default_opts}
NameError: name 'test_object' is not defined
My understanding is that the variables resolution_default_opts and ee_default_opts are static class variables and that I should be able to call them via test_object. or self., but evidently it is wrong.
What is causing this error and how could I still get foo to work as intended (ie return ee_default_ops when we input 'ee') ?
If it is relevant, the python version is : Python 3.8.10
Within the scope of the class definition, the class itself hasn't been defined yet (hence the NameError), but the class variables are in the local scope, so you can refer to them directly.
Within the scope of the method definition, the class variables aren't in the local scope, but at the time the method is executed the class has been defined so you can use that to refer to them (you can also use self).
class test_object:
resolution_default_opts = {'e':{'ylims':[0,2],'xlabel':'resolution'},'t':{'ylog':True,'xlabel':'resolution'}}
ee_default_opts = {'eee':{'xlabel':'parameter number'}}
default_opts = {'resolution':resolution_default_opts,'ee':ee_default_opts}
def __init__(self, name):
self.name = name
def foo(self, i):
return test_object.default_opts[i]
an_instance = test_object('a_name')
print(an_instance.foo('ee'))
Related
I'm trying to instantiate a class within a function, then call a method within the class inside the same function, like this:
# Define the class
class myclass:
def __init__(self,string_to_print):
self.string_to_print = string_to_print
def myclass_func(self):
print(self.string_to_print)
# Define the function that utilizes the class
def func(class,func,str)
instance = class(str)
class = class.func()
# Run the function that utilizes the class
func(myclass,myclass_func,str)
But I am getting an error like "'myclass' object is not callable". Why is this? Additionally, I expect my 'class = class.func()' line is wrong; if it is, what is the correct way to call the method from the recently instantiated class?
Edit: fixed mistake in class declaration
You can't use method names as global variables. If you want to call a method dynamically, pass its name as a string and use the getattr() function.
# Define the class
class myclass:
def __init__(self,string_to_print):
self.string_to_print = string_to_print
def myclass_func(self):
print(self.string_to_print)
# Define the function that utilizes the class
def func(class,func,str)
instance = class(str)
return getattr(instance, func)()
# Run the function that utilizes the class
func(myclass,'myclass_func',str)
Define your class using the class keyword rather than def.
Create an instance of the class.
Define a function that will try to execute the function given by its name.
class myclass:
def __init__(self,string_to_print):
self.string_to_print = string_to_print
def myclass_func(self):
print(self.string_to_print)
myclass_instance = myclass('Hello world')
def execute_function(instance, function):
getattr(instance, function)()
execute_function(myclass_instance, 'myclass_func')
Output:
Hello world
This is the given code:
class Person:
def __init__(self, name):
self.name = name
def greeting(self):
return "hi, my name is " + self.name
some_person = Person("yeabsira")
print(some_person.greeting())
However, I was expecting the syntax in which the constructor method uses like:
class Name:
def __init__(self,atribute1,atribute2):
self.atribute1=atribute1
self.atribute2=atribute2
new_instance=Name("example")
print(new_instance.atribute1)
expect answer="example"
So my question is how "some_person.greeting()" symantic works?
some_person.name returns the value of the person's name, while some_person.greeting() returns a greeting with the name, it is just a function defined within the class Person and works normally like any other function. You could use some_person.name if you only need the name.
However, by using some_person.greeting(), you implemented data hiding, which hides internal object details, and the user (in the future) only needs to access greeting() and not the value of name itself.
I don't understand why this code works, I think it should give an error but it doesn't:
class Person:
def __init__(self, name):
self.name = name
def greeting(self):
# Should return "hi, my name is " followed by the name of the Person.
return "hi, my name is "+self.name
def __str__(self):
return "str"
some_person = Person("foo")
print(print(some_person.greeting()))
In the __init__ method, I try to access a variable that is not actually defined (shouldn't be an error?)
and in the greeting method, I try to access that variable which is just defined in __init__ (shouldn't it be an error?). It doesn't give any error and works smoothly? how is that possible?
def __init__(self, name):
self.name =name
In fact, you tried to access a variable that is actually defined. name is defined in function parameter.
It shouldnt be an error! That's because self is the Object itself (you see why its called self?) so if you define something in the __init__ function, it is stored inside the object so you can use it from another function by peeling it off the object itself. The name variable you were talking about is defined in the function definition! Where you put __init__(self,name)
I have the following class and class variables:
class MyClass:
class_var_1 = "a"
class_var_2 = run_class_method()
#classmethod
def run_class_method(cls):
return "ran class method"
However, the interpreter says that run_class_method isn't defined. Using MyClass.run_class_method() doesn't work either. Coming from a java background, I don't understand why this doesn't work. So, how can I fix it?
Additionally, I discovered that this works if I define class variables at the end of the class. Is this considered bad practice in python?
Class body in python is an executable context, not like Java that only contains declaration. What this ultimately means is that sequence of execution is important within a class definition.
To quote the documentation:
class definition is an executable statement.
...
The class’s suite is then executed in a new execution frame (see Naming and binding), using a newly created local namespace and the original global namespace. (Usually, the suite contains mostly function definitions.) When the class’s suite finishes execution, its execution frame is discarded but its local namespace is saved. [4] A class object is then created using the inheritance list for the base classes and the saved local namespace for the attribute dictionary. The class name is bound to this class object in the original local namespace.
Some more lengthier explanations.
If you want to call a function to define a class variable, you can do it with one of these ways:
use staticmethod:
class MyClass:
def _run_instance_method():
return "ran instance method"
run_instance_method = staticmethod(_run_instance_method)
class_var_1 = "a"
class_var_2 = _run_instance_method() # or run_instance_method.__func__()
or define it as a standalone function:
def run_method():
return "ran method"
class MyClass:
class_var_1 = "a"
class_var_2 = run_method()
# optional
run_method = staticmethod(run_method)
or access the original function with __func__ and provide a dummy cls value:
class MyClass:
#classmethod
def run_class_method(cls):
return "ran class method"
class_var_1 = "a"
class_var_2 = run_class_method.__func__(object())
or set the class variables after class creation:
class MyClass:
#classmethod
def run_class_method(cls):
return "ran class method"
class_var_1 = "a"
MyClass.class_var_2 = MyClass.run_class_method()
MyClass is not yet defined when its class attributes are still being defined, so at the time class_var_2 is being defined, MyClass is not yet available for reference. You can work around this by defining class_var_2 after the MyClass definition block:
class MyClass:
class_var_1 = "a"
#classmethod
def run_class_method(cls):
return "ran class method"
MyClass.class_var_2 = MyClass.run_class_method()
The first thing to note is that Java does not have class methods. It has static methods and regular methods. A regular method receives the instance it was called from as an argument. A class method receives the class is was called from (not the class it is defined on) as an argument. Static methods get nothing special and act like normal functions -- static methods are just a way of grouping logically related methods.
The second thing to note is that a Java class definition is parsed into a separate class definition and an implicit static constructor. When initialising class attributes this enables you to call methods before they are defined in the class body. This is because in the actual program these statements will be called only after the class has been created/loaded into memory. In Python there is no such distinction. Instead, to create a class you execute a series of statements inside a specialised namespace, and this is then used to create the class. Like in a body of a function or module block of code you cannot use a variable before it is exists. This includes using the class within the class body (as it doesn't exist yet!)
eg. This is valid Java:
class X {
static int i = 1;
static X obj = newInstance();
// ^-- executed after the class has been created, but is still being initialised.
static X newInstance() {
return new X();
}
}
But this is not valid Python
class X:
val = 1
obj = new_instance()
# ^-- We're still in the body of X, and neither new_instance nor X has been created yet
#classmethod
def new_instance(cls):
return cls()
# even if new_instance was defined before obj, Python still wouldn't be able to fill
# in the cls argument as X still doesn't exist when new_instance is first invoked
In Python you must do the static construction of your class explicitly. Bear in mind this is exactly what would happen in Java, it's just hidden behind syntactic sugar.
class X:
val = 1 # this can still be done in the class body as it doesn't need the class
obj = None # not necessary, but can help type checkers know that X has an
# attribute obj -- you can use type annotations to further help
#classmethod
def new_instance(cls):
return cls()
# explicit class initialisation of attributes
X.obj = X.new_instance()
Another way to do this would be to define a parent class that has control over the creation of its subclasses (or a metaclass). Below, we use __init_subclass__ in a parent class to set the attribute during class creation.
class InitVar():
def __init_subclass__(cls, varname, funcname, **kwargs):
class_method = getattr(cls, funcname)
setattr(cls, varname, class_method())
class MyClass(InitVar, varname="class_var_2", funcname="run_class_method"):
class_var_1 = "a"
#classmethod
def run_class_method(cls):
return "ran class method"
print(MyClass.class_var_2)
# ran class method
I'm trying to call a function from a Class thats name will change depending on what type of enemy is being instantiated. How can I accomplish this?
My attempt was this: AssignClass.[self.Class](self)
but obviously that sintax makes no sense
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
# Using a variable function call to remove
# the need for a ton of 'if' statements
AssignClass.[self.Class](self)
# Basically automate doing this:
if self.Class = "Orc":
AssignClass.Orc(self)
elif self.Class = "Wizard"
AssignClass.Wizard(self)
class AssignClass:
def Orc(Creature):
Creature.Class='Orc'
Creature.Health=100
Creature.Mana=0
def Wizard(Creature):
Creature.Class='Wizard'
Creature.Health=75
Creature.Mana=200
Evil_Wizard = Creature("Evil Wizard", "Wizard")
You can retrieve class methods using getattr() and then just pass your Creature instance, e.g.:
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
getattr(AssignClass, Class)(self)
Btw. this is everything but a recommended style for Python classes, the least of which is that you're shadowing the Creature class in your AssignClass (which shouldn't be a class in the first place). I'd recommend you to read the PEP 8 -- Style Guide for Python Code thoroughly.
Played around a little more and found that I can use eval for this. (Safe as no user input can be added here)
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
eval('AssignClass.'+Class)(self)