I know this question has been asked several times, I did find some post on this forum, but they don't seem to work, that's why I ask again.
Basically I have to create a number of a graph and the code is below
fig1 = go.Figure()
fig1.update_layout(showlegend = True, xaxis_title = "Time(s)")
and I would like to tidy it up using a for loop, therefore replacing the number with a variable, but doing something like below doesn't work
exec('"fig"+str(i) = go.Figure()')
I receive
SyntaxError: cant' assign to operator
How can I tidy this number of "same" code into a neat form, please?
*I also know that there is a danger using exec if this is not the way to go, what would be the better python way, please?
Edit:
This is my final working code, thanks for everyone's input
for i in range(5):
figures.update({f'fig{i}':go.Figure()})
eval('figures["fig'+str(i)+'"]').update_layout(showlegend = True, xaxis_title = "Time(s)")
In here, I can have control of the number of variable to be created (this code to be run. Also, putting it into the normal for loop form allows me to do more other things alone with the current figure. Lastly, the dictionary method allows a name to be linked to the figure so provides convenience
Why not try this:
figures = []
for i in range(loop):
figures.append(go.Figure())
#at this point you will now have n figures each of which you can reference
This way you still can find all of your plots if you know which order you made them in. Otherwise you could simply use a dict. I think its cleaner than making 10 diffrent variables.
Related
I feel guilty for asking question I can not properly name because I can not name the pattern used in the code.
There is a code on github I'm trying to understand and failing to do so.
https://github.com/kivy-garden/speedmeter/blob/master/kivy_garden/speedmeter/init.py
Pattern I don't understand is in lines 128,129 and 181, 182 and many other places.
Big picture is.
There a class
class MyClassName(Widget):
there are methods e.g.
def _draw_this_and_that(self):
self._someName.clear()
add = self._someName.add
add(Color(rgba=get_color_from_hex(color)))
This "_someName" found in whole code only in those 2 places as my code sample.
I understand that
add = self._someName.add
creates function "add" but why that is needed? why not calling
self._someName.add
instead?
I guess that
self._someName.clear()
does erase whatever was added to "_someName", right?
I completely do not understand how
add(Color(rgba=get_color_from_hex(color)))
does it job (but it does) and then whatever is drawn will be with that color.
Do I guess it right that if I need to change color (if some condition met) then I could just add different color?
add(Color(rgba=get_color_from_hex(different_color)))
and don't stress that adding will cause memory leak because
self._someName.clear()
will take care of it?
I never seen such pattern. I'd be very happy if someone could explain how it works and why.
Thank you in advance!
The _somenameIG are canvas instruction groups that are created in the __init__() method:
add = self.canvas.add
for instructionGroupName in _ig:
ig = InstructionGroup()
setattr(self, '_%sIG' % instructionGroupName, ig)
add(ig)
So, the self._someName.clear() is clearing a canvas instruction group, and the add() method adds instructions to the group.
In addition to the other answer I would like to address the following:
Pattern I don't understand is in lines 128,129 and...
According to its documentation the attr. sectors in line 130 is a list of alternating values and colors a copy of which is stored locally by l and then the color value(s) is(are) passed to the InstructionGroup. Since it is used as a redrawing mechanism, the instruction groups are removed from canvas by the method clear before drawing again.
creates function "add" but why that is needed? why not calling...
This is done mainly to prevent the re-evaluation process.
i want to read separate table from images in python. i have 10 images (b1,b2,b3....b10) with 8 bands in which I have to proceed this functions
b1 = rasterio.open('B1.tif')
b2 = rasterio.open('B2.tif')
b3 = rasterio.open('B3.tif')
...
b10 = rasterio.open('B10.tif')
I started to try this with just one loop and the update. but this error appears
b1 = rasterio.open('B1.tif')
for i in range(1,8):
b1_(i) = b1.read(i)
File "<ipython-input-4-c7fe8df05d17>", line 3
b1_(i) = b1.read(i)
^
SyntaxError: cannot assign to function call
I guess you assume, that the python interpreter will resolve b1_(i) to e.g. b1_10 if i equals 10 in that loop. That however is not part of the python language definition. In fact, and as the error states, this syntax looks like a function invocation to the interpreter (of the function b1_ with argument i).
For your code to be able to be interpretet, (but be aware, this is a bad habit of coding and I will suggest a possible solution below,) you could replace b1_(i) = b1.read(i) by eval("b1_"+str(i)+"b1.read("+str(i)+")"). eval is a function, which takes a string and evaluates it as a line of code.
As mentioned, this is bad coding style. Code using eval is much harder to read, as this simple example should already sufficiently show. Furthermore, eval is in some situations susceptible to code injection, which may lead to security issues. (Especially, if eval is used to execute a piece of code, which contains data entered from a user, who could use this fact to inject malicious code on his own.) There are also other issues, but to discuss that further would unnessesarily bloat this answer; you may consider doing some research on yourself here.
So how to do it right? The best way would be to use an array of values instead of many different variables. You can do that in python like this:
fp_b1 = rasterio.open('B1.tif')
store_b1 = []
for i in range(1,9):
store_b1[i] = fp_b1.read(i)
Note, that I had also to change the parameters of range, since range will not include stop.
As array generally start with index 0 in python (and many other programming languages), this code would probably be written rather like this:
fp_b1 = rasterio.open('B1.tif')
store_b1 = []
for i in range(8):
store_b1[i] = fp_b1.read(i+1)
This probably has been answered somewhere, but sadly I don't really know which keywords to look for, so let me describe what I mean with "internal" for loop via this code snippet that I'm using:
#htf.measures(*(htf.Measurement("{}".format(i)) for i in ["v1", "v2"]))
Here, the for loop is used in a way that doesn't work the way I've known it to do (til now), example:
for i in x:
#Do something
It seems like a very handy way to use a for loop, so I'd like to understand how it works. I could imagine the * operator to have something to do with it, but as far as I can see its only a multiplicator, right?
This is what I am trying to do with the "new" for function:
tmp = [].append(*("P{}".format(id) for id in range(1, 5, 1)))
This returns an Error, saying append doesn't take 4 arguments, so I guess the for loop works somehow, but what I meant to do was to make it append 4 items.
Finally: I have two questions, 1. What is this for loop called and how can I google it, 2. how do I make the "append 4 times" thingy work?
I don't know how to phrase this, but my current structure uses recursion. So I call upon the same function when the looping within the function is finished, and use the results of the loop as an argument to continue the program with new data to work through.
So this function of mine spits out a number, among other things. I've assigned this resulting number from the function to a variable.
My gap in knowledge is however coming up short on the thought process/logic to a solution. Although it sounds simple. I've thought about maybe a way to over ride the original information with a global variable of the same name, but that doesn't seem to over ride it since at the top of the function is the variable declaration. I've also thought about using arguments with the *args method. But that isn't working either.
My function, has a loop in it. That requires variable X for example. On the loops first run, X already has a base number to work with. This X is used in the loop as part of the equation, after all the loops run through, I get a result and assign that to variable Y.
I need to essentially add Y to X, essentially making X = X + Y and then recall the function.
Trying this line of thinking, and declaring an updated variable doesn't work with this method because when I recall the function, it updates it to the base number again.
I've also explored if statements checking if the number is X and declaring the updated variable in the else statement otherwise. But that too is not providing a fruitful outcome.
What line of thinking, or method should I be applying to solve this problem?
I can explain more if necessary, me being a new to Python I've come up short on the search for the answer before posting, maybe because I don't know the terminology for this type of method to find such answers.
I appreciate your time in reading this, thanks!
def loop1Yo(f):
matrixDict = {}
timer = 0
x = f[0]
for element in f[1:]:
depTime = 1479215120 + xyzDuration ###(This part wouldnt work but I think explains what I am trying to do.)
directionResult = (depTime) + (element)
sortedMatrixList = sorted(matrixDict.items(), key=operator.itemgetter(1))
xyz = sortedMatrixList
xyzDuration = sortedMatrixList[0][1]
f = [x for x,_ in xyz]
print (f)
loop1Yo(f)
so i know this is a bit of a workaround and theres probably a better way to do this, but heres the deal. Ive simplified the code from where tis gathering this info from and just given solid values.
curSel = nuke.selectedNodes()
knobToChange = "label"
codeIn = "[value in]"
kcPrefix = "x"
kcStart = "['"
kcEnd = "']"
changerString = kcPrefix+kcStart+knobToChange+kcEnd
for x in curSel:
changerString.setValue(codeIn)
But i get the error i figured i would - which is that a string has no attribute "setValue"
its because if i just type x['label'] instead of changerString, it works, but even though changer string says the exact same thing, its being read as a string instead of code.
Any ideas?
It looks like you're looking for something to evaluate the string into a python object based on your current namespace. One way to do that would be to use the globals dictionary:
globals()['x']['label'].setValue(...)
In other words, globals()['x']['label'] is the same thing as x['label'].
Or to spell it out explicitly for your case:
globals()[kcPrefix][knobToChange].setValue(codeIn)
Others might suggest eval:
eval('x["label"]').setValue(...) #insecure and inefficient
but globals is definitely a better idea here.
Finally, usually when you want to do something like this, you're better off using a dictionary or some other sort of data structure in the first place to keep your data more organized
Righto, there's two things you're falling afoul of. Firstly, in your original code where you are trying to do the setValue() call on a string you're right in that it won't work. Ideally use one of the two calls (x.knob('name_of_the_knob') or x['name_of_the_knob'], whichever is consistent with your project/facility/personal style) to get and set the value of the knob object.
From the comments, your code would look like this (my comments added for other people who aren't quite as au fait with Nuke):
# select all the nodes
curSel = nuke.selectedNodes()
# nuke.thisNode() returns the script's context
# i.e. the node from which the script was invoked
knobToChange = nuke.thisNode()['knobname'].getValue()
codeIn = nuke.thisNode()['codeinput'].getValue()
for x in curSel:
x.knob(knobToChange).setValue(codeIn)
Using this sample UI with the values in the two fields as shown and the button firing off the script...
...this code is going to give you an error message of 'Nothing is named "foo"' when you execute it because the .getValue() call is actually returning you the evaluated result of the knob - which is the error message as it tries to execute the TCL [value foo], and finds that there isn't any object named foo.
What you should ideally do is instead invoke .toScript() which returns the raw text.
# select all the nodes
curSel = nuke.selectedNodes()
# nuke.thisNode() returns the script's context
# i.e. the node from which the script was invoked
knobToChange = nuke.thisNode()['knobname'].toScript()
codeIn = nuke.thisNode()['codeinput'].toScript()
for x in curSel:
x.knob(knobToChange).setValue(codeIn)
You can sidestep this problem as you've noted by building up a string, adding in square brackets etc etc as per your original code, but yes, it's a pain, a maintenance nightmare, and starting to go down that route of building objects up from strings (which #mgilson explains how to do in both a globals() or eval() method)
For those who haven't had the joy of working with Nuke, here's a small screencap that may (or may not..) provide more context: