Variable not found in the outer class scope - python

I have this code:
class Attributes(object):
class __metaclass__(type):
def __init__(self, cls_name, cls_bases, cls_dict):
# super(Attributes.__metaclass__, self) # NameError: global name 'Attributes' is not defined
super(__metaclass__, self).__init__(
cls_name, cls_bases, cls_dict)
gives
NameError: global name '__metaclass__' is not defined
Why __metaclass__ variable is not found in the outer scope?

try this instead
super(Attributes.__metaclass__, self).__init__(cls_name, cls_bases, cls_dict)

While creating a class, only its name is visible. Its contents does not exist yet until the class is finished being created. The parts inside the class, therefore, cannot access any fields of the class during creation. So you will need to use fully qualified names to denote that you want to access fields of the class
You are currently creating a class SimpleModel and while doing so you are creating a class Attributes and while doing so a class __metaclass__. Since while you do this, the class SimpleModel isn't existing yet, the method __init__ is not yet part of anything existing. It first gets created and then, later, will be made part of the class __metaclass__. Hence it cannot know the identifier __metaclass__. And since __metaclass__ also never becomes a global, when called, this identifier cannot be known.
That's the reason why you have no __metaclass__ in your scope at this point, but when later __init__ gets called, only a SimpleModel is available via the global scope, so you can base your name on it: SimpleModel.Attributes.__metaclass__.

Looks like the answer is this:
Outer scope for a method is not class body, but outer function in which the class is contained or the module.
That's why in the case of
class Test():
attr_1 = 'smth'
def a_method(self):
attr_1 # not visible on this line
attr_1 is not visible inside method Test.a_method
And the solution is to define metaclass at global level:
class AttributesMeta(type):
def __init__(self, cls_name, cls_bases, cls_dict):
super(__metaclass__, self).__init__(
cls_name, cls_bases, cls_dict)
class Attributes(object):
__metaclass__ = AttributesMeta

Related

Passing attribute to parent class via child in decorator

I have this class hierarchy
class ParentMeta(type):
def __new__(v1, name, b, x):
# some code
return super(ParentMeta, v1).__new__(v1, name, b, x)
and then
class ServiceMeta(ParentMeta, AnotherServiceMeta):
pass
The ServiceMeta is normally used as a decorator in add_metaclass i.e.,
#add_metaclass(ServiceMeta)
class MyService(object):
""" Code
The question i want to ask that how can i pass name attribute here? The name attribute is part of ParentMeta.
EDIT:
Link to add_metaclass documentation: https://six.readthedocs.io/#six.add_metaclass
By default, the ServiceMeta is picking up MyService as name for the class. I want to modify this behavior. For that reason i want to pass name value in decorator.

What is the corretly way to call super in dynamically added methods?

I defined a metaclass which add a method named "test" to the created classes:
class FooMeta(type):
def __new__(mcls, name, bases, attrs):
def test(self):
return super().test()
attrs["test"] = test
cls = type.__new__(mcls, name, bases, attrs)
return cls
Then I create two classes using this Metaclass
class A(metaclass=FooMeta):
pass
class B(A):
pass
When I run
a = A()
a.test()
a TypeError is raised at super().test():
super(type, obj): obj must be an instance or subtype of type
Which means super() cannot infer the parent class correctly. If I change the super call into
def __new__(mcls, name, bases, attrs):
def test(self):
return super(cls, self).test()
attrs["test"] = test
cls = type.__new__(mcls, name, bases, attrs)
return cls
then the raised error becomes:
AttributeError: 'super' object has no attribute 'test'
which is expected as the parent of A does not implement test method.
So my question is what is the correct way to call super() in a dynamically added method? Should I always write super(cls, self) in this case? If so, it is too ugly (for python3)!
Parameterless super() is very special in Python because it triggers some behavior during code compilation time itself: Python creates an invisible __class__ variable which is a reference to the "physical" class statement body were the super() call is embedded (it also happens if one makes direct use of the __class__ variable inside a class method).
In this case, the "physical" class where super() is called is the metaclass FooMeta itself, not the class it is creating.
The workaround for that is to use the version of super which takes 2 positional arguments: the class in which it will search the immediate superclass, and the instance itself.
In Python 2 and other occasions one may prefer the parameterized use of super, it is normal to use the class name itself as the first parameter: at runtime, this name will be available as a global variable in the current module. That is, if class A would be statically coded in the source file, with a def test(...): method, you would use super(A, self).test(...) inside its body.
However, although the class name won't be available as a variable in the module defining the metaclass, you really need to pass a reference to the class as the first argument to super. Since the (test) method receives self as a reference to the instance, its class is given by either self.__class__ or type(self).
TL;DR: just change the super call in your dynamic method to read:
class FooMeta(type):
def __new__(mcls, name, bases, attrs):
def test(self):
return super(type(self), self).test()
attrs["test"] = test
cls = type.__new__(mcls, name, bases, attrs)
return cls

Multi-Level nested classes with inheritance among inner classes

Let me give a brief explanation of the issue:
I have a server object with several functionalities.
all functionalities have some common code, so this warrants a functionalities base class
Each functionality has its own set of constants, defined in a constants class within the functionality.
The functionality base class has a set of common constants as well.
here is a sample code:
class server:
class base_func:
class common_consts:
name = "name"
def validate(self):
pass
def execute(self):
pass
class func1(base_func):
class consts:
new_name = base_func.common_consts.name #this is where the problem occurs
def get_result(self):
self.validate()
self.execute()
so when i try to use the common_consts from base_func, in func1.consts, I get the following error:
NameError: global name 'base_func' is not defined
I do not know why this happens. Can someone help?
Is there a limitation to the scope of nesting in python, especially 2.7
Also if i remove the top level server class, and have the functionality classes as independent classes, everything seems to work fine. The example of the working code is here:
class base_func:
class common_consts:
name = "name"
def validate(self):
pass
def execute(self):
pass
class func1(base_func):
class consts:
new_name = base_func.common_consts.name #this is where the problem occurs
def get_result(self):
self.validate()
self.execute()
This leads me to believe that there definitely exists some limitation to the nesting depth and namespace scopes in python. I just want to be sure before i make changes to my design.
class server:
class base_func:
class common_consts:
name = "name"
def validate(self):
pass
def execute(self):
pass
# base_func and func1 are at same, level. So, here you can directly use base_func and func1 anywhere
# at top level of the server class
class func1(base_func):
class consts:
new_name = base_func.common_consts.name # this is where the problem occurs
def get_result(self):
self.validate()
self.execute
For a class(classes have their own namespace), variable lookup works like this:
While parsing the class body any variable defined inside the class body can be access directly, but once
it is parsed it becomes a class attribute.
As, the class base_func is inside server class which is still being parsed the func1(base_func) will work fine.
But, for class consts base_func is not at the same level. So, after looking the variable in its body it will directly jump
to its enclosing scope, i.e global namespace in this case.
A fix will be to do the assignement like this:
class server:
class base_func:
class common_consts:
name = "name"
def validate(self):
pass
def execute(self):
pass
class func1(base_func):
class consts:
pass
def get_result(self):
self.validate()
self.execute
func1.consts.new_name = base_func.common_consts.name
You've hit a problem with class scope. The class scope is never made available except to operations that directly occur in the class scope. This is why you can't call method within another method without referencing self.
eg.
class A(object):
def f(self):
pass
def g(self):
f() # error, class scope isn't available
The same is true when you create a nested class. The initial class statement class ClassName(Base): has access to the class scope, but once in the nested class scope you lose access to the enclosing class scope.
Generally, there is no good reason to nest classes in python. You should try to create all classes at module level. You can hide internal classes by either placing them in a sub module or all prefixing them with an underscore.
class _BaseFunctionality(object):
# common constants
name = "name"
value = "value"
def execute(self):
return (self.name, self.value)
class _SpecificFunctionality(_BaseFunctionality):
# specific constants
# override value of value attribute
value = "another_value"
def get_result(self):
assert self.name == "name"
return self.execute()
class Server(object):
functionality = _SpecificFunctionality()
assert _BaseFunctionality.value == "value"
assert _SpecificFunctionality.value == "another_value"
assert Server().functionality.get_result() == ("name", "another_value")

Accessing a parent variable from a nested child class in Python

I'm wondering what the best way of accessing a parent variable from a nested subclass is, currently I'm using a decorator.
Is that the only/best way???
I don't want to have to directly access the parent variable (eg. ComponentModel.origin (see below)) as that would require more code in the "config" file, so I'm wondering whether I could assign parent variable in a class which the subclass in question inherits from?
Trivial example of my current solution:
# defined in a big library somewhere:
class LibrarySerialiser(object):
pass
# defined in my module:
class ModelBase:
pass
class SerialiserBase(LibrarySerialiser):
def __init__(self, *args, **kwargs):
# could i some how get hold of origin here without the decorator?
print self.origin
super(SerialiserBase, self).__init__(*args, **kwargs)
def setsubclasses(cls):
cls.Serialiser.origin = cls.origin
return cls
# written by "the user" for the particular application as the
# configuration of the module above:
#setsubclasses
class ComponentModel(ModelBase):
origin = 'supermarket'
class Serialiser(SerialiserBase):
pass
ser = ComponentModel.Serialiser()
This is obviously a trival example that misses all the real logic hence lots of the classes appear void but are really necessary.
FYI, the accepted terminology used when nesting classes as you've done is inner/outer, not parent/child or super/subclass. The parent/child or super/sub relationship refers to inheritance. This makes your decorator's name, setsubclasses, confusing, since there are no subclasses involved!
The unusual thing you're doing here is using the class as a namespace without instantiating it. Normally you would instantiate your ComponentModel and at that time, it is trivial to give your Serialiser inner class a copy of an attribute from its outer class. E.g.:
class ModelBase(object):
def __init__(self):
self.Serialiser.origin = self.origin
# ... then
cm = ComponentModel()
ser = cm.Serialiser()
Better yet, have the outer class instantiate the inner class and pass it a reference to the outer class; then it can grab any attributes it wants itself, whenever it needs them:
class ModelBase(object):
def __init__(self, *args, **kwargs):
serialiser = self.Serialiser(self, *args, **kwargs)
class SerialiserBase(LibrarySerialiser):
def __init__(self, outer, *args, **kwargs):
self.outer = outer
print self.outer.origin
super(SerialiserBase, self).__init__(*args, **kwargs)
# ...
cm = ComponentModel()
ser = cm.serialiser
However, if you insist on being able to get this attribute without instantiating the outer class, you can use a metaclass to set the attribute:
class PropagateOuter(type):
def __init__(cls, name, bases, dct):
type.__init__(cls, name, bases, dct)
if "Serialiser" in dct:
cls.Serialiser.outer = cls
class ModelBase(object):
__metaclass__ = PropagateOuter
# Python 3 version of the above
# class ModelBase(metaclass=PropagateOuter):
# pass
class SerialiserBase(LibrarySerialiser):
def __init__(self, *args, **kwargs):
print self.outer.origin
super(SerialiserBase, self).__init__(*args, **kwargs)
class ComponentModel(ModelBase):
origin = 'supermarket'
class Serialiser(SerialiserBase):
pass
ser = ComponentModel.Serialiser()
This isn't doing anything your decorator isn't, but the user gets it automatically through inheritance rather than having to specify it manually. The Zen of Python says "explicit is better than implicit" so tomato, tomato.
You could even write the metaclass so that it introspects the outer class and puts a reference to that class into every inner class regardless of their name.
By the way, one of the pitfalls of the way you're doing this is that all your model classes must subclass SerialiserBase. If a user of your class just wants the default serialiser, they can't just write Serialiser = SerialiserBase in their class definition, they must write class Serialiser(SerialiserBase): pass. This is because there's only one SerialiserBase and it obviously can't contain a reference to multiple outer classes. Of course, you could write your metaclass to deal with this (e.g. by automatically making a subclass of the specified serialiser if it already has an outer attribute).

Issue in calling the base class methods

I am newbie in Python.I know this question is useless.But I am frustrated with my issue.I have 2 methods defined in my base class Animals.When I try to call the base methods in my inherited classes it shows these error:
NameError: name 'display_name' is not defined
class Animals():
def display_name(self):
print ('Name is Mr.X')
def display_age(self):
print('Age is 25')
class Name(Animals):
display_name(self)
class Age(Animals):
display_age(self)
n=Name()
a=Age()
n.display_name()
a.display_age()
You need to refer to the display_name function with a self prefix instead of passing self as an argument.
Also, as noted by Antimony, you need to call the display_name from within a function that is associated with an instance of the class (inside a function that accepts the self argument).
Code that appears outside a method function but inside a class is associated with the whole class, not with any particular instance of that class - using the self variable in this context has no meaning - if you create multiple objects from the class which one does it refer to?
class Animals():
def display_name(self):
print ('Name is Mr.X')
def display_age(self):
print('Age is 25')
class Name(Animals):
def call_display_name(self):
self.display_name()
class Age(Animals):
def call_display_name(self):
self.display_age()
Name().call_display_name()

Categories