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).
Related
I have a class with an __init__ method where I define some variables, as well as a method AddSignalSpectrum:
class SpectrumGraph:
# Properties
def __init__(self):
self.PlotHeight= 300
self.PlotWidth = 800
self.xRangeMin = -10
self.xRangeMax = 10
self.yRangeMin = -0.1*(self.xRangeMax - self.xRangeMin)
self.yRangeMax = 0.9*(self.xRangeMax -self.xRangeMin)
def AddSignalSpectrum(
self,
name,
Type,
CenterPosition,
Bandwidth=2,
Height = self.yRangeMax*0.8,
Color = "#0000FF",
LineWidth = 2,
AbscissaSymbol = '$\omega_0$',
ShowLegend = False):
# Rest of the method
What I want is to provide the default value of self.yRangeMax*0.8 to the method but when I do that I get an error saying that 'self' is not defined. I can use attributes of self inside the method just fine, but it does not seem to work within the arguments of the method definition. I assume this is some sort of name space problem but can't find a workaround. It could be I'm just not sure what exactly to search for.
This is not initialization, it is setting defaults for function parameters. The problem isn't to do with namespaces, it's to do with binding.
These values are determined once, ahead of time and are stored as part of the function (or method) object. It is not possible to use attributes of the self parameter here because there isn't one at the time that this happens. It is the same thing with ordinary functions:
def example(a, b=a+3): # doesn't work; `a` is not defined ahead of time
print(f'a={a} and b={b}')
The normal workaround for this is to use None as the default value, and then explicitly check for this in the logic and replace None with the desired calculation:
def example(a, b=None): # works
if b is None:
b = a + 3
print(f'a={a} and b={b}')
# and now we can call:
example(1) # 'a=1 and b=4'
The usual way to do this is to set a sentinel value (idiomatically None) and then define behavior inside the method, e.g.:
def Dog:
def __init__(self, name, friend):
self.name = name
self.friend = friend
def say_hello(self, other=None):
if other is None:
other = self.friend
print(f"Hello {other}, I'm {self.name}! How are you? Bark!")
I have noticed the following in setting a class variable:
from ingest.models import WBReport
wb=WBReport()
wb.date = '2019-01-09'
The above does not set the date for the class. For example, calling this method, it prints None:
#classmethod
def load_asin(cls):
print cls.date
However, if I add another method to set that variable it does work. For example:
#classmethod
def set_date(cls, date):
cls.date=date
from ingest.models import WBReport
wb=WBReport()
wb.set_date('2019-01-09')
Why does the first method (wb.date=X)not work but the second one (wb.set_date(X)) does?
Instance variables and class variables exist separately. wb.date = '2019-01-09' sets an instance variable on the object wb, not the class variable WBReport.date, which is what the class method set_date sets.
The call to the class method is roughly equivalent to WBReport.date = '2019-01-09'.
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.
I'm new to classes, this is a small piece of code I've written, but I'm still really shaky on this concept, and am wondering exactly how the method node_name comes into play here and if it's even needed?
from rdflib import BNode
class HigherNode(object):
def node_name(name):
return name
def __init__(self, **kwargs):
self.node_type = kwargs.get('node_type', 'cog_con')
self.position = kwargs.get('position', 0)
self.node_id = self.node_name
self.node = kwargs.get(self.node_name(), BNode())
for key, value in kwargs.items():
setattr(self, key, value)
def __str__(self):
return 'This is the node of {} in the graph'.format(self.node_id)
I behavior that I'm seeking is something equivalent to this:
elephant = BNode()
when used as:
some_node = HigherNode(node_id = 'elephant')
So, first off, methods have to be called by an instance of the class. So, your behavior would look something like this:
# create an instance
node = HigherNode()
# get the name
print node.node_name()
However, you never declared name inside the class. So, you'll have to do something like this:
def node_name(self):
return self.name
(All instances pass a reference to themselves to thier functions when called, so you'll always have to have at least one variable in the function call. You don't have to call it self.)
Really, it looks like what you want is actually a name setter/getter.
Try this:
Declare/set the variable in __init__.
def __init__(self, **kwargs):
self.node_name= kwargs.get('node_name', None)
Then you can use the variable like this:
# create an instance
node = HigherNode()
# get the name
print node.node_name
# set the name
node.node_name = "bluh"
Since your class extends object, use getter/setter properties.
#property
def node_name(self):
return self.node_name
#node_name.setter
def node_name(self, x):
self.node_name = str(x)
These are called exactly the same as above in option 1:
# create an instance
node = HigherNode()
# get the name
print node.node_name
# set the name
node.node_name = "bluh"
I prefer this method, since it allows you much more control over how things are set, or even whether or not you can set or get them! (Just make a getter property without a corresponding setter property, for instance.)
However, this second method is more work to set up and may not be suitable for simple variables.
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