How to access variable of a nested functions in python? - python

I need to access a variable which is set in a nested function. I'm reading this variable data from another thread. What is the best way to obtain the data without doing any scope violation?
Here is the code snippet,
class main(object):
def sub_fun(self):
def inner_fun(self):
self.var = 10
inner_fun(self)
p = main().sub_fun()
Now how to access the var attribute of p?

class main(object):
def sub_fun(self):
def inner_fun(self):
self.var = 10
p = main()
You cannot access the nested function's variable because its a closure & is accessible only to the immediate parent. var is only accessible by inner_fun. See below for a different implementation.
class main(object):
def outer_function(self):
def inner_function(self):
self.x = 10
return self.x
out = inner_function(self) # Accessing the inner function
return out
p = main()
q = p.outer_function()
print(q)
If there is a need for a class object or a parent function to access a variable inside a child nested function, then the variable should be hoisted to the outer scope by returning it. Otherwise the scope of the variable will reside only in the so called child nested function.

The problem is that main.sub_fun returns None. There are two simple workarounds:
Just store a reference to the item you create before calling the method:
p = main()
p.sub_fun()
print(p.var)
This is the best option in my opinion.
If you really want the one line version to work, return self from sub_fun:
def sub_fun(self):
def inner_fun():
self.var = 10
inner_fun()
return self
print(main().sub_fun().var)
In neither case do you need to pass self to inner_fun. It will always look into the outer scope when the local name is not found. #2 shows an example of this.

Related

Unexpected behaviour of python list.append() in the context of inheritance

I inherit features from one parent class (__Parent) to two different child classes for constructing a nested data structure. The init() method of ChildTwo initiallizes the parent class using super() thereby setting i_am_from_child to "Two". It then appends an instance of ChildOne to the inherited list var. It is this appending that behaves unexpected when using list.append(). The init() of ChildOne also initializes the parent class in the same way, setting its i_am_from_child to "One", but without appending its inherited list var.
Therfore the list var of the instance of ChildOne stored in ChildTwo's var[0] should have a length of 0 as it is empty. This behaviour is obtained when using numpy append. However pythons list.append() results in an instance of ChildOne beingg strored at that location.
import numpy as np
class __Parent:
var = []
i_am_from_child = None
def __init__(self, which:str=None) -> None:
self.i_am_from_child = which
print(f"Parent initiallized by Child{which}")
def how_long(self) -> int:
return len(self.var)
def from_child(self) -> str:
return self.i_am_from_child
class ChildOne(__Parent):
def __init__(self) -> None:
print("Initiallizing ChildOne")
super().__init__(which="One")
print(f"ChildOne: len(self.var) = {len(self.var)}")
class ChildTwo(__Parent):
def __init__(self) -> None:
print("Initiallizing ChildTwo")
super().__init__(which="Two")
# two options in question
self.var.append(ChildOne()) # this behaves unexpected
#self.var = np.append(self.var, ChildOne()) # this behaves as expected
#####################################################################
X = ChildTwo() # instance of ChildTwo
Y = X.var[0] # copy of ChildOne instance created by ChildTwo constructor
print(f"type(X) = {type(X)} and type(Y) = {type(Y)}")
print(f"X is from Child{X.from_child()} and Y=X.var[0] is from Child{Y.from_child()}")
# something weird with var happens when using diffent append methods
print()
print(f"ChildOne: self.function() = {Y.how_long()} should be 0")
print(f"ChildTwo: self.function() = {X.how_long()} should be 1")
print(f"Type of Y.var[0] is {type(Y.var[0])}")
Using print() I checked the correct sequence of method calles, additionally the types are correct. But Y.var[0] should be empty, i.e. [] and thus should have length zero. Commenting out the python append and uncommenting the numpy append statements in ChildTwo.init() produces the desired behaviour.
It's because you're declaring var as a class variable, which is shared between all instances of that class, rather than as an instance variable. When you type self.var, python first looks in the instance attribute dictionary, and if an attribute with the name var doesn't appear there, it looks in the instance's class dictionary, where it finds the shared var attribute. If you want each instance to treat them separately, you need to assign that variable in the __init__ method.
class Parent:
def __init__(self):
self.var = []

Calling a method which uses variables from another method in a class

I'm writing a script in Maya and trying to assign a variable inside another method instead of inside __init__ like we normally do. I put my window commands inside the __init__ so when a new instance is initialized, if we assign variables inside __init__ it will run all the way through without assigning the variables the value I want.
For example:
def __init__(self, windowID = 'selectsomething', title = 'select something'):
self.windowID = windowID
self.title = title
if cmds.window(self.windowID, exists = True):
cmds.deleteUI(self.windowID)
myWindow = cmds.window(self.windowID, title = self.title)
cmds.rowColumnLayout(numberOfColumns = 2, columnWidth = [(1, 300), (2, 100)],
columnAttach = [1, 'left', 10], columnSpacing = [1, 10],
rowSpacing = [1, 5])
cmds.button(label = "Apply", command = self.applyButton) #it should print out selected object
cmds.button(label = "Cancel", command = self.cancelButton)
cmds.showWindow(myWindow)
#now for example if I have a sel variable to store selected objects, it would actually return nothing cuz the code ran all the way through and didnt wait for me to select what I needed
sel = cmds.ls(selection = True) #this will return nothing
My solution is to assign variables inside the applyButton method, which will store what I selected. I found it's a bit weird to call a method which uses a variable from another method from that method itself.
For example:
class Abc:
def func(*arg):
print(self.var)
def var(self):
self.var = 1
self.func()
abc = Abc()
abc.var()
For now the code runs, but doesn't it sound a bit weird to call the func method which uses the variable from var method from var method itself?
I haven't seen anyone do it. I mean without class, a variable assigned inside a function stays inside that function and we can only use it with return. But it looks like in class we can make any inside-method-variable global by adding self. before it?
My friend also said we can use #property in this case but I don't think I figured it out yet.
You need to be aware that data attributes are implicitly a member of the class and can therefore be used by any method in the same class after they have been assigned somewhere.
From the Python documentation:
Data attributes need not be declared; like local variables, they
spring into existence when they are first assigned to.
While your example will work (if you call Abc.var() before Abc.func()), it would IMHO be better style to initialize the variable at class level like this (note that I call it val to avoid confusion with the method name):
class Abc:
val = 0
def func(self, *arg):
print(self.val)
def var(self):
self.val = 1
self.func()
The purpose of the self keyword is nicely explained here and here.
Also note that initializing an attribute inside the __init__ method will bind this attribute to a class instance, whereas initializing it outside of the __init__ method will bind it to the class itself, which is a different thing (see this discussion).

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.

Share data between an external function and a python class function

I want to share a variable value between a function defined within a python class and an externally defined function. So in the code below, when the internalCompute() function is called, self.data is updated. How can I access this updated data value inside a function that is defined outside the class, i.e inside the report function?
Note:
I would like to avoid using of global variable as much as possible.
class Compute(object):
def __init__(self):
self.data = 0
def internalCompute(self):
self.data = 5
def externalCompute():
q = Compute()
q.internalCompute()
def report():
# access the updated variable self.data from Compute class
print "You entered report"
externalCompute()
report()
It's a good idea to avoid magic globals, but your report() function must have some way to know where to look. If it's ok to pass it the object doing the computation, i.e. q, it can simply print out q.data. If not, then you can arrange for q to save its data in the class itself-- obviously this means that the class can only be instantiated once, so I would go with the first option.
You can't do that unless you instantiate the class somewhere.
Currently in your implementation you instantiate the Compute class here:
def externalCompute():
q = Compute()
q.internalCompute()
But as soon as the function finishes q goes out of scope and is destroyed so you will lose all information that the class contains. In order to do what you want to do the Compute class has to be instantiated to not be local to a function (or your function needs to return the instance of the Compute class as to preserve its state.)
Usually you would do that by having one "main" if your python file in this way:
class Compute(object):
def __init__(self):
self.data = 0
def internalCompute(self):
self.data = 5
def externalCompute(q):
q.internalCompute()
def report(q):
# access the updated variable self.data from Compute class
data = q.data
print "You entered report"
if __name__ == '__main__':
q = Compute()
externalCompute(q)
report(q)
You actually have access to the data through q.data, you just have to return it.
Change your code to reflect that fact:
class Compute(object):
def __init__(self):
self.data = 0
def internalCompute(self):
self.data = 5
def externalCompute():
q = Compute()
q.internalCompute()
return q.data
def report():
print externalCompute()
report() # 5
If you don't like this approach, you have only a few other options:
Global variable.
Instantiating another class.
Updating the same class you instantiated.
Database.
Pickle.

How do I reference the object passed to my class when I instantiate?

I have this class
class SECHeader(object):
def __init__(self,header_path):
self.header = open(header_path).read()
I have some methods in this class, one of the methods I am trying to do needs to parse the name
def parsed_name(self):
return header_path.split('-')[-1]
This works fine if in my code I use the name header_path to identify the thing I am trying to operate on
for header_path in header_paths:
header = SECHeader(header_path)
print header.parsed_name()
But if I change the name
for named_path in header_paths:
header = SECHeader(named_path)
print header.parsed_name()
I get a NameError
I played around - if can use any name for the object in the parsed_name function as long as I use the same name for the object I want to process but I can't seem to figure out how to name it so a user does not have to use my naming scheme
specifically if I change the parsed_name function to
def parsed_name(self):
return banana.split('-')[-1]
and in my loop if change it to
for banana in header_paths:
header = SECHeader(banana)
print header.parsed_name()
it works like a charm but that limits the portability of this thing I am working on. as any user would have to reference the path with whatever label I use in the function.
The problem here is that you have header_path declared as a variable for the init function. It's scope is local to the init function.
What you need is to associate header_path as a variable for the class instance.
Class SECHeader(object):
def __init__(self,header_path):
self.header_path = header_path # Instantiate a variable for class object
self.header = open(header_path).read()
def parsed_name(self):
return self.header_path.split('-')[-1] # Call the associated variable
Another way is to actually call the variable that you gave as an argument to SECHeader in parsed_name. This variable name would be in the class namespace.
for banana in header_paths:
header = SECHeader(banana)
print header.parsed_name()
Class SECHeader(object):
def __init__(self,header_path): # header_path takes value of banana
# and ends scope in __init__
self.header = open(header_path).read()
def parsed_name(self):
return banana.split('-')[-1] # banana was passed to class and is known

Categories

Resources