How to reference another nested class from within nested class - python

I have the following nested structure:
from peewee import *
class Parent:
...
class A:
name = TextField()
class B:
from_A = ForeignKeyField(A)
I am trying to reference class A within a ForeignKeyField within class B, but both A and Parent.A return a name not found error. What is the proper way to reference class A from within class B? Is this possible?

At the time of definition of class B, class Parent is not fully defined so it cannot be used: at definition time, you can only use:
global variables
variables belonging to the element being defined
But you have no access to variables defined in an enclosing block be them classes or not.
So you are left with only two options:
define class B outside of Parent
initialize the element at run time after everything has been defined (in that sense, run time starts immediately the end of the class Parent block`):
class Parent:
...
class A:
name = TextField()
class B:
...
Parent.B.from_A = ForeignKeyField(Parent.A)

Related

In Python, when does the class name become bound? I.e. when/where can we reference the class name w/o getting a `NameError`?

The Python documentation says:
the following will fail:
class A:
a = 42
b = list(a + i for i in range(10))
I ran it and it and sure enough it fails: it throws NameError: name 'a' is not defined. So I tried to fix by using A.a instead of a in the list argument:
class A:
a = 42
b = list(A.a + i for i in range(10))
but that still didn't work. It throws NameError: name **'A'** is not defined.
When does the class name (in this case the A identifier) become bound such that we can reference it? I know that referencing A within a method of A is valid because when Python is parsing? the class definition, it doesn't execute the class methods. As an example, the following works:
class A:
a = 42
# b = list(A.a + i for i in range(10))
def foo(self):
print(A)
#classmethod
def bar(cls):
print(A)
#staticmethod
def baz():
print(A)
a = A()
a.foo()
a.bar()
a.baz()
A is not bound until the class definition block completes. To quote the tutorial:
When a class definition is left normally (via the end), a class object is created. This is basically a wrapper around the contents of the namespace created by the class definition; we’ll learn more about class objects in the next section. The original local scope (the one in effect just before the class definition was entered) is reinstated, and the class object is bound here to the class name given in the class definition header (ClassName in the example).

Python linear inheritance, middle class being overridden by child class on super() property calls

I have a class setup like so:
class A(object):
__configuration = {}
def __init__(self, configuration: dict = {}):
A.__configuration = configuration
#property
def configuration(self):
return A.__configuration
class B(A):
def __init__(self, configuration: dict = {}):
super().__init__(configuration=configuration)
#property
def configuration(self):
return super().configuration.get('mammals')
class C(B):
def __init__(self, configuration: dict = {}):
super().__init__(configuration=configuration)
#property
def configuration(self):
return super().configuration.get('human')
Now here is where I am running into something that I can't quite get around.
When I instantiate class C, it will call the super init to class B. Then, class B in turn calls a super init to class A.
The configuration will get set as a class variable in class A, and the property method when called will return that configuration/dict/class variable.
Class B I am attempting to have the configuration property get the super (class A's) configuration, then retrieve the 'mammals' value from it.
Class C then in turn I am attempting to (when called from class C) get the 'human' value from it's parent, which will call the class B's property, which I hoped would in turn call the class A's property... Thus resulting in a key / value lookup of:
get entire config
get the mammal value (which is another config/dict)
get the human value (type doesn't matter)
Problem:
When I instantiate class C, it propagates up to class A on instantiation as it should. Class A's configuration is set correctly. Then, in class B, when I reference that super().configuration, it ends up returning my CHILD (class C) 's configuration.
For example, if I put this line in my init in class B and let the instantiation carry out from instantiation class C:
print(self.configuration)
it would return to me the value for the human dictionary, NOT the mammals dictionary which is what I would like it to.
Here is an example of a configuration I would pass in when instantiating Class C:
config = {
'mammals': {
'human': {
...
}
...
}
...
}
exampleInstantiation = C(configuration=config)
I have been trying to look up linear inheritance, the ordering for the class method resolutions but I can't find anything that is giving me insight on why the MIDDLE class is being overridden by the child class.
Any help is appreciated.

How to refer to class methods when defining class variables in 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

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)

Class instance as class variable in python

I am making a class which I would like to have as a class member of a separate class so that all instances of this second class can access the first class. At the moment I have something which looks like this:
class A:
def __init__(self):
print "In Constructor!"
class B:
ClassA = ''
def __init__(self, InstanceA):
self.ClassA = InstanceA
However, I get complaints saying "str object has no attribute..." when I try and use the ClassA class variable. Is there a way to construct class B with an argument of InstanceA and then somehow set it to be a class variable? Or any other solution for that matter!
You are not assigning the class attribute in B.__init__, just setting a new instance attribute - B.ClassA is still '' (None would be a more sensible starting value, by the way).
The minimal fix is:
class B:
ClassA = None
def __init__(self, InstanceA):
self.__class__.ClassA = InstanceA # or B.ClassA = ...
However, note that this changes the class attribute every time you create a new instance.
Based on your comments, something like this may be more helpful:
class B:
ClassA = None
#classmethod
def set_class_attr(cls, instance):
cls.ClassA = instance
Which you can then use like:
B.set_class_attr(A()) # set class attribute once
b = B() # don't need to pass in new A instances when creating B instances
b.ClassA. # ...
Depending on the behaviour you want, you can check in that classmethod whether the instance has already been set and e.g. raise an error if the user tries to reset it.

Categories

Resources