How to refer to class methods when defining class variables in Python? - python

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

Related

How to ref class static method in current class variable?

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

Static initializer for Python classes

I'm looking for an equivalent to the static { ... } block in Java that can be used in Python classes. Specifically, I want to be able to access static resources like the arguments of the class constructor and store them in a field of the class, like so:
class A:
constructor_args = A.__init__.__code__.co_varnames
def __init__(self, foo=0, bar=1):
...
This example doesn't work, because class A is not yet initialized when I call A.__init__.__code__.co_varnames.
My current workaround is to alter the static field after the class has been created like so:
class A:
constructor_args = ...
def __init__(self, foo=0, bar=1):
...
constructor_args = A.__init__.__code__.co_varnames
But this solution is rather ugly because I change a static field of a class outside of the class context and if the class contains a lot of code, it's easy to miss out on what is going on here.
So basically I need a way to call a function right after the class has been initialized, and I want to define this function inside of the class.
You will have to at least define the __init__ method first, but you can access its properties immediately after:
class Foo:
def __init__(self, bar, baz):
pass
constructor_args = __init__.__code__.co_varnames
Inside the class block code executes inside its own namespace, so __init__ is directly accessible as __init__.
Here is a simple approach that postpones execution of code that needs the finished class by moving it inside a function defined inside the class body. To have the function called and deleted after use we define a simple decorator:
import inspect
def finalizing(cls):
cls.__finalize__(cls)
del cls.__finalize__
return cls
#finalizing
class example:
def __finalize__(me):
me.constructor_args = list(inspect.signature(me.__init__).parameters)
def __init__(self, x):
pass
example.constructor_args
# ['self', 'x']
You could use a class decorator:
def store_constructor_args(cls):
cls.constructor_args = cls.__init__.__code__.co_varnames
return cls
#store_constructor_args
class A:
def __init__(self, foo=0, bar=1):
x = 10
print(A.constructor_args)
# ('self', 'foo', 'bar', 'x')
#store_constructor_args
class A:
is equivalent to
class A:
...
A = store_constructor_args(A)

Access class static variable from super class in python

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.

Class instance as static attribute

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 __init__ (not instance __init__)

Here's a very simple example of what I'm trying to get around:
class Test(object):
some_dict = {Test: True}
The problem is that I cannot refer to Test while it's still being defined
Normally, I'd just do this:
class Test(object):
some_dict = {}
def __init__(self):
if self.__class__.some_dict == {}:
self.__class__.some_dict = {Test: True}
But I never create an instance of this class. It's really just a container to hold a group of related functions and data (I have several of these classes, and I pass around references to them, so it is necessary for Test to be it's own class)
So my question is, how could I refer to Test while it's being defined, or is there something similar to __init__ that get's called as soon as the class is defined? If possible, I want self.some_dict = {Test: True} to remain inside the class definition. This is the only way I know how to do this so far:
class Test(object):
#classmethod
def class_init(cls):
cls.some_dict = {Test: True}
Test.class_init()
The class does in fact not exist while it is being defined. The way the class statement works is that the body of the statement is executed, as a block of code, in a separate namespace. At the end of the execution, that namespace is passed to the metaclass (such as type) and the metaclass creates the class using the namespace as the attributespace.
From your description, it does not sound necessary for Test to be a class. It sounds like it should be a module instead. some_dict is a global -- even if it's a class attribute, there's only one such attribute in your program, so it's not any better than having a global -- and any classmethods you have in the class can just be functions.
If you really want it to be a class, you have three options: set the dict after defining the class:
class Test:
some_dict = {}
Test.some_dict[Test] = True
Use a class decorator (in Python 2.6 or later):
def set_some_dict(cls):
cls.some_dict[cls] = True
#set_some_dict
class Test:
some_dict = {}
Or use a metaclass:
class SomeDictSetterType(type):
def __init__(self, name, bases, attrs):
self.some_dict[self] = True
super(SomeDictSetterType, self).__init__(name, bases, attrs)
class Test(object):
__metaclass__ = SomeDictSetterType
some_dict = {}
You could add the some_dict attribute after the main class definition.
class Test(object):
pass
Test.some_dict = {Test: True}
I've tried to use classes in this way in the past, and it gets ugly pretty quickly (for example, all the methods will need to be class methods or static methods, and you will probably realise eventually that you want to define certain special methods, for which you will have to start using metaclasses). It could make things a lot easier if you just use class instances instead - there aren't really any downsides.
A (weird-looking) alternative to what others have suggested: you could use __new__:
class Test(object):
def __new__(cls):
cls.some_dict = {cls: True}
Test()
You could even have __new__ return a reference to the class and use a decorator to call it:
def instantiate(cls):
return cls()
#instantiate
class Test(object):
def __new__(cls):
cls.some_dict = {cls: True}
return cls
You can also use a metaclass (a function here but there are other ways):
def Meta(name, bases, ns):
klass = type(name, bases, ns)
setattr(klass, 'some_dict', { klass: True })
return klass
class Test(object):
__metaclass__ = Meta
print Test.some_dict
Thomas's first example is very good, but here's a more Pythonic way of doing the same thing.
class Test:
x = {}
#classmethod
def init(cls):
# do whatever setup you need here
cls.x[cls] = True
Test.init()

Categories

Resources