I am creating a couple of 'Child' classes which are quite similar and thus wanted to group common methods in a parent class. My problem comes when trying to access static variables of the child classes from the super class.
The following code throws the error: NameError: name 'var' is not defined
class Parent:
#staticmethod
def do_something_with_var():
print(var)
class Child(Parent):
var = "Hello world"
Child.do_something_with_var()
Next thing I tried was obviously to declare var in Parent, but the same error persists.
class Parent:
var = ""
#staticmethod
def do_something_with_var():
print(var)
class Child(Parent):
var = "Hello world"
Child.do_something_with_var()
A solution that I found was to receive the sender class, but then the call becomes a bit ugly:
class Parent:
#staticmethod
def do_something_with_var(cls):
print(cls.var)
class Child(Parent):
var = "Hello world"
Child.do_something_with_var(Child)
You are confused over the usage of staticmethods(your third attempt), accessing class variables (second attempt).
What you want to do is classmethods, as shown under (Python 2.7):
class Parent(object):
#classmethod
def do_something_with_var(cls):
print cls.var
class ChildA(Parent):
var = "Child A"
ChildA.do_something_with_var() # prints Child A
This example is equivalent to your third attempt.
Static scoping of the method means it is bound to the class scope as a namespace, without any reference to the class.
What you need is to have a #classmethod, so that you are able to get a reference to the class.
Related
I want a program to call a specific class based on a parameter/variable value. However, I don't want to use any clunky if-statements. My first thought was to use the globals() function, but I couldn't get it to work. Here's an example:
class SomeClass:
def __init__():
print("Hello, world!")
class OtherClass:
def runClass(className):
# Call class based on variable className
The reason I want to do this is because there is a wide variety of classes may need to be called, and so just piling up if-statements in my code won't do it. Any help would be greatly appreciated. Thanks!
Here's how you can call a class via globals
class SomeClass:
def __init__(self):
print("Hello, world!")
def __call__(self):
return "SomeClass called"
class OtherClass:
def runClass(self, className):
globals()[className]()()
o = OtherClass()
result = o.runClass("SomeClass")
print(result)
Notice, I am instantiating and then calling it via the __call__ special method, which is the closest match to your description I could think of.
Use a dict.
name_to_class = dict(some=SomeClass,
other=OtherClass)
def factory(name):
klass = name_to_class(name)
return klass()
some_obj = factory("some")
other_obj = factory("other")
One way to solve this problem is to use a dictionary to map the values of the variable className to the corresponding class.
Try this exemple :
class SomeClass:
def init(self):
print("Hello, world!")
class OtherClass:
def init(self):
print("Goodbye, world!")
classNameToClass = {
"SomeClass": SomeClass,
"OtherClass": OtherClass
}
def runClass(className):
# Call class based on variable className
cls = classNameToClass[className]
return cls()
runClass("SomeClass") # prints "Hello, world!"
runClass("OtherClass") # prints "Goodbye, world!"
Here, the dictionary classNameToClass maps the string names of the classes (e.g. "SomeClass") to the corresponding class objects (e.g. SomeClass). Then, in the runClass function, we look up the class object using the value of the className variable, and call it to create an instance of the class.
I've found an answer. The parameter that governs the called class can just be assigned elsewhere. At first, I thought it would need some complex function, but in reality, I guess the question didn't give enough details. The class itself only uses items from whatever object is given. So, instead of having to dynamically call a class, it's as simple as:
class SomeClass:
def printHelloWorld():
print("Hello, world!")
class OtherClass:
def __init__(self, usingClass):
self.object = usingClass
def doThis():
usingClass.printHelloWorld()
x = OtherClass(SomeClass())
x.doThis()
It's on me for not giving enough information. Thank you all for your help.
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
does anyone have idea how to refer to class static method in the class attributes? for example:
class A(object):
var = A.staticMethod1 # this line doesn't work, as A has not be defined yet.
#staticmethod
def staticMethod1(*param):
pass
Class blocks are just temporary scopes, so, it's actually pretty simple. Just move the alias below the method definition, and take off the class name (since you're in scope directly)
class A(object):
#staticmethod
def staticMethod1(*param):
pass
var = staticMethod1
Python 3 doesn't allow you to reference a class inside its body (except in methods):
class A:
static_attribute = A()
def __init__(self):
...
This raises a NameError in the second line because 'A' is not defined.
Alternatives
I have quickly found one workaround:
class A:
#property
#classmethod
def static_property(cls):
return A()
def __init__(self):
...
Although this isn't exactly the same since it returns a different instance every time (you could prevent this by saving the instance to a static variable the first time).
Are there simpler and/or more elegant alternatives?
EDIT:
I have moved the question about the reasons for this restriction to a separate question
The expression A() can't be run until the class A has been defined. In your first block of code, the definition of A is not complete at the point you are trying to execute A().
Here is a simpler alternative:
class A:
def __init__(self):
...
A.static_attribute = A()
When you define a class, Python immediately executes the code within the definition. Note that's different than defining a function where Python compiles the code, but doesn't execute it.
That's why this will create an error:
class MyClass(object):
a = 1 / 0
But this won't:
def my_func():
a = 1 / 0
In the body of A's class definition, A is not yet defined, so you can't reference it until after it's been defined.
There are several ways you can accomplish what you're asking, but it's not clear to me why this would be useful in the first place, so if you can provide more details about your use case, it'll be easier to recommend which path to go down.
The simplest would be what khelwood posted:
class A(object):
pass
A.static_attribute = A()
Because this is modifying class creation, using a metaclass could be appropriate:
class MetaA(type):
def __new__(mcs, name, bases, attrs):
cls = super(MetaA, mcs).__new__(mcs, name, bases, attrs)
cls.static_attribute = cls()
return cls
class A(object):
__metaclass__ = MetaA
Or you could use descriptors to have the instance lazily created or if you wanted to customize access to it further:
class MyDescriptor(object):
def __get__(self, instance, owner):
owner.static_attribute = owner()
return owner.static_attribute
class A(object):
static_attribute = MyDescriptor()
Using the property decorator is a viable approach, but it would need to be done something like this:
class A:
_static_attribute = None
#property
def static_attribute(self):
if A._static_attribute is None:
A._static_attribute = A()
return A._static_attribute
def __init__(self):
pass
a = A()
print(a.static_attribute) # -> <__main__.A object at 0x004859D0>
b = A()
print(b.static_attribute) # -> <__main__.A object at 0x004859D0>
You can use a class decorator:
def set_static_attribute(cls):
cls.static_attribute = cls()
return cls
#set_static_attribute
class A:
pass
Now:
>>>> A.static_attribute
<__main__.A at 0x10713a0f0>
Applying the decorator on top of the class makes it more explicit than setting static_attribute after a potentially long class definition. The applied decorator "belongs" to the class definition. So if you move the class around in your source code you will more likely move it along than an extra setting of the attribute outside the class.
class Something(object):
our_random = Something.random_thing
#staticmethod
def random_thing():
return 4
Of course, this doesn't work, becauese Something doesn't exist when I attempt to call its method. Nor does this:
class Something(object):
our_random = random_thing
#staticmethod
def random_thing():
return 4
I've "solved" this by Just placing random_thing()'s definition above the class, but I find this messy.
Call it in the .__init__() initializer then:
class Something(object):
def __init__(self):
self.our_random = Something.random_thing()
or call the static method after you defined it, but are still defining the class; because it is a static method, you'd have to access it through the __func__ attribute:
class Something(object):
#staticmethod
def random_thing():
return 4
our_random = random_thing.__func__()
If you didn't mean to call it, just create a copy of the method with a different name, just do so after you defined it:
class Something(object):
#staticmethod
def random_thing():
return 4
our_random = random_thing # our_random as an alias for random_thing
The class body is executed as a function, with the local namespace of the function then forming the class attributes. So, like a function, if you want to refer to other objects you need to make sure they are defined first.
class Something(object):
#staticmethod
def random_thing():
return 4
our_random = random_thing
Class definitions create a namespace, so you can refer to other names (class attributes) within the class body without needing to access them through the class.