How to reference a static attribute from within a class in Python? - python

I have the following python snippet:
class myClass:
myVar = 'a'
def __init__(self):
self.myOtherVar = 'b'
myVar = 'c' # Gets assigned but only as a local variable.
print myVar # prints 'a' !
print self.myOtherVar # says 'self' not found
My question is this;
What's the proper way to print the contents of myVar from within myClass and/or re-assign them from init?

The problem you are facing is because you don't understand how the scoping of class declarations work. A class declaration is executed in its own scope. After the execution is completed a new class object is created and the obtained scope is attached to the class as its __dict__.
Note: the class scope is not searched from within the methods scopes! This means that you have to reference class attributes as MyClass.attribute when inside a method definition.
For example:
class MyClass:
var = 1
# we are executing this code as a single block
# so you must reference the variable as is usual
print(var)
# default values are *not* inside the definition.
# they are evaluated in the outer scope, so use plain "var" here
def method(self, a_default=var):
print(a_default)
def other_method(self):
# inside methods you are in a different scope
print(MyClass.var)
# equivalent *if* no "var" instance attributes exists
print(self.var)
Note: since the class doesn't still exist when executing its declaration you cannot refer to MyClass at the "top level" of MyClass declaration:
class MyClass:
var = 1
print(MyClass.var) # error: MyClass still doesn't exist.
A side effect of this, is that the following code:
class MyClass:
x = 1
results = list(i+x for i in range(10))
Produces:
NameError Traceback (most recent call last)
<ipython-input-6-f1d4417b2e52> in <module>()
----> 1 class MyClass:
2 x = 1
3 results = list(i+x for i in range(10))
4
<ipython-input-6-f1d4417b2e52> in MyClass()
1 class MyClass:
2 x = 1
----> 3 results = list(i+x for i in range(10))
4
<ipython-input-6-f1d4417b2e52> in <genexpr>(.0)
1 class MyClass:
2 x = 1
----> 3 results = list(i+x for i in range(10))
4
NameError: name 'x' is not defined
Because generator expressions (and list-comprehensions in python3) are, in fact, considered functions with their own scope. Since the class scope isn't searched from inner function scopes the x cannot be found.
You can word around this using a function definition and default values:
class MyClass:
x = 1
def _make_results(x=x):
return list(i+x for i in range(10))
results = _make_results()
del _make_results # otherwise it would be added as a method.
# or:
results = (lambda x=x: list(i+x for i in range(10)))()
This isn't usually a problem since class definitions rarely contain anything other than method definitions and a few constants.
There are already a few questions on SO about class scopes:
The scope of names defined in class block doesn't extend to the methods' blocks. Why is that?
Short Description of the Scoping Rules?
Nested classes' scope?
Variable scopes in python classes
Why static binding works differently for class and function?

self.var will:
give var in self.__dict__ if present
give var in self.__class__.__dict__ if present
AttributeError
So use this or self.__class__.var if you want to access the static variable minding inheritance. If you extend myClass, the children instances will access the static variable in the child class.
If you want to access the static variable in myClass even when called from descendants, use myClass.var.
As for reassigning them, this must be done explicitly on the class object, or the assignment will just target the instance.

class myClass:
myVar = 'a'
def __init__(self):
self.myOtherVar = 'b'
print myVar # -> 'a'
class EmptyClass: pass
s = EmptyClass()
__init__(s)
myVar = s.myOtherVar
print myVar # -> 'b'
print myClass.myVar # -> 'b'

Related

See function stop from locating defined class in Python

I'd like to define a class inside a function (for testing purpose) and put a value into
a function variable:
def foo():
myvar = None
class myclass:
def run(self):
myvar = 5
mm = myclass()
mm.run()
print(myvar)
The above prints None
Is there any way other than global to make the myvar variable accessible from the class? The correct answer would print 5
It's not possible to assign a value to a variable outside the current scope without global. If you need to persist the value within the class you can define class variables instead. Example:
def foo():
class Class:
var_to_change = None
def run (self):
self.var_to_change = 5
print (Class.var_to_change)
instance = Class()
instance.run()
print (Class.var_to_change)
I haven't tested the above code but it should work in theory.

Why can't python third-level nested classes inherit from each other?

This code is valid in python:
class Inner:
class InnerInner:
attr = 1
class Inner2(Inner):
class InnerInner2(Inner.InnerInner):
attr = 2
Why is the following not possible in python?
class Outer:
class Inner:
class InnerInner:
attr = 1
class Inner2(Inner):
class InnerInner2(Inner.InnerInner):
attr = 2
It gives me following error:
class InnerInner2(Inner.InnerInner):
NameError: name 'Inner' is not defined
I would expect that Inner would be completely defined before definition of Inner2 starts.
Same reason this fails:
class Foo:
a = 1
def bar(self):
return a
Foo().bar()
Just like code inside bar can't see anything inside Foo's scope, code inside Inner2 can't see anything from Outer's scope.
Inner is completely defined by the point you try to use it, but it's in a scope you can't access.

Class Level Constructor for the Python Class

I understand that __init__() is the constructor for an instance of the class. It is called whenever a class is instantiated. There is also the concept of class variables -- variables that belong to the class and shared by all instances. For example:
class A(object):
a = 1
b = [] #a and b are shared by all instances of class A
But the problem is that sometimes it takes more code to initialize a and b than the one lines showing above. Therefore there is a need for 'class level constructor.' I wonder, though, if there is such a thing.
You can just do something like
class A(object):
# .......
var = input()
var += 10
# ecc.
A.b = var

defining variables inside a function belonging to a class

In a code, there is a class that has a function named 'goal_callback'. In the function, variables are defined using .init prefix and others are defined normally without the prefix.
I know that the self. prefix is used to make the variable a 'class variable' so that it can be accessible to every function in class. So in the code, I have, only one function present, does it make any difference if we define the variables with the self. prefix or not.
What exactly will be the difference between the '_pub_takeoff' variable and the 'takeoff_or_land' variable?
#! /usr/bin/env python
class CustomActionMsgClass(object):
def __init__(self):
self._as = actionlib.SimpleActionServer("action_custom_msg_as", CustomActionMsgAction,
self.goal_callback, False)
def goal_callback(self, goal):
success = True
r = rospy.Rate(1)
self._pub_takeoff = rospy.Publisher('/drone/takeoff', Empty, queue_size=1)
self._takeoff_msg = Empty()
self._land_msg = Empty()
# Get the goal word: UP or DOWN
takeoff_or_land = goal.goal #goal has attribute named 'goal'.
if __name__ == '__main__':
rospy.init_node('action_custom_msg')
CustomActionMsgClass()
rospy.spin()
Here is an example for object-level and class-level variables.
class A(object):
Z = 3 # class variable. Upper-case is good code style for class-level variables
# defined inside the class but outsize of it's methods
def __init__(self):
self.x = 1 # object variable
y = 2 # local variable; it will lost after returning from the function
def some_method(self):
self.w = 4 # object variable too
# Note that it is not recommended to define
# object variables outside of __init__()
In your code _pub_takeoff is variable of the object; takeoff_or_land is local variable.

Python Instantiate Class Within Class Definition

I am attempting to add a variable to a class that holds instances to the class. The following is a shortened version of my code.
class Classy :
def __init__(self) :
self.hi = "HI!"
# "CLASSIES" variable holds instances of class "Classy"
CLASSIES = []
for i in xrange(0,4) :
CLASSIES.append(Classy())
Upon running the code, I get the following error.
Traceback (most recent call last):
File "classy.py", line 6, in Classy
CLASSIES.append(Classy())
NameError: name 'Classy' is not defined
Is there another way to add instances of a class to a class/static variable within that class?
The class body is executed before the class is created. Therefore, you are attempting the instantiate the class before it exists. You can still attach instances to the class, but you have to create them after the class body finished, e.g.:
class Classy(object):
def __init__(self):
self.hi = "HI!"
CLASSIES = []
for i in xrange(4):
Classy.CLASSIES.append(Classy())
However, I'd suggest you first think long and hard whether you actually need this effectively-global list, and whether you need it to be part of the class object. Personally, I almost never do something like this.
The simplest way to do this is do it after the class is created, when the class has been defined, and therefore can be used:
class Classy :
CLASSIES = []
def __init__(self) :
self.hi = "HI!"
Classy.CLASSIES = [Classy() for _ in xrange(0,4)]
(Here using a list comprehension for convinience, as it's the most readable and efficent way to build a list).
Also note that if this intended to be a constant, you should probably make it a tuple rather than a list, and if it isn't intended to be, you should probably not use an ALL_CAPS name which, by convention, implies a constant.
It seems to me that you want to obtain that:
class Classy :
CLASSIES = []
def __init__(self) :
self.hi = "HI!"
Classy.CLASSIES.append(self)
for i in xrange(4):
Classy()
for x in Classy.CLASSIES:
print x
result
<__main__.Classy instance at 0x011DF3F0>
<__main__.Classy instance at 0x011DF440>
<__main__.Classy instance at 0x011DF418>
<__main__.Classy instance at 0x011DF2B0>
EDIT
Note that with the code of Lattyware:
class Classy :
CLASSIES = []
idC = id(CLASSIES)
def __init__(self) :
self.hi = "HI!"
#Classy.CLASSIES.append(self)
Classy.CLASSIES = [Classy() for _ in xrange(0,4)]
print Classy.idC
print id(Classy.CLASSIES)
print 'Classy.idC==id(Classy.CLASSIES) :',Classy.idC==id(Classy.CLASSIES)
result
18713576
10755928
Classy.idC==id(Classy.CLASSIES) : False
While with the for loop of delnan'code, it doesn't appear.
However it's easy to correct:
writing
Classy.CLASSIES[:] = [Classy() for _ in xrange(0,4)]
or
Classy.CLASSIES.extend(Classy() for _ in xrange(0,4))
instead of
Classy.CLASSIES = [Classy() for _ in xrange(0,4)]
it depends of what is desired.
EDIT 2
Methods may reference global names in the same way as ordinary
functions. The global scope associated with a method is the module
containing its definition. (A class is never used as a global scope.)
http://docs.python.org/2/tutorial/classes.html#class-definition-syntax
A class has a namespace implemented by a dictionary object. Class
attribute references are translated to lookups in this dictionary,
e.g., C.x is translated to C.__dict__["x"]
http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes
class Classy :
CLASSIES = []
print '"CLASSIES" in globals()',"CLASSIES" in globals()
print '"CLASSIES" in Classy.__dict__ ==',"CLASSIES" in Classy.__dict__
result
"CLASSIES" in globals() False
"CLASSIES" in Classy.__dict__ == True
Delnan, how will you continue to pretend that CLASSIES is global ??
Did I misunderstood something in your debate with Lattyware ?
The class itself is not defined until after the class block finishes executing, so you can't make use of the class inside its own definition.
You could use a class decorator or a metaclass to add your desired class variable after the class is created. Here's an example with a decorator.
def addClassy(cls):
cls.CLASSIES = [cls() for a in xrange(4)]
return cls
#addClassy
class Classy(object):
pass
>>> Classy.CLASSIES
0: [<__main__.Classy object at 0x000000000289A240>,
<__main__.Classy object at 0x000000000289A518>,
<__main__.Classy object at 0x000000000289A198>,
<__main__.Classy object at 0x000000000289A208>]

Categories

Resources