I'm trying to wrap my head around the way positional and keyword arguments work in python, and, it seems, I'm failing rather miserably.
Given a function with a call signature matplotlib.pyplot.plot(*args,**kwargs), it can be called as
import matplotlib.pyplot as plt
x=[1,2,3]
y=[5,6,7]
plt.plot(x,y,'ro-')
plt.show()
Now, I'm trying to wrap it into something which I can call as mplot(x,y,'ro-',...) where ... are whatever arguments the original function was ready to accept. The following fails miserably, but I can't really figure how to fix it:
def mplot(x,y,fmt,*args,**kwargs):
return plt.plot(x,y,fmt,*args,**kwargs)
mplot(x,y,'ro-')
Any pointers to a way out would be very much appreciated.
You need it this way:
def mplot(x,y,fmt,*args,**kwargs):
#do stuff with x, y and fmt
return plt.plot(*args,**kwargs)
I'm assuming that your intention is to consume the x, y and fmt in your mplot routine and then pass the remaining parameters to plt.plot.
I don't believe that this is actually what you want (I can see that plt.plot wants to receive x, y and fmt and so they should not be consumed). I had deleted this answer but since your posted code apparently works, I'll leave this visible for a little while and see if it provokes the real question to be revealed!
Related
I'm working on a problem and want to plot multiple functions that include other functions:
from pylab import *
import numpy as np
import matplotlib as plt
r_x = 14e-2
l_m = np.arange(2e-5, 4e-5, 1e-6)
def first(l_m, r_x):
first = 1.03 * (l_m/r_x)
return first
plt.pyplot.figure()
plt.pyplot.plot(l_m, first(l_m, r_x))
plt.pyplot.xlabel("l_m")
plt.pyplot.ylabel("first")
plt.pyplot.show()
and this works properly, but once I try including my first() in another function like so:
def second(a_x, first):
second = 0.5*((a_x**2)/(0.25*(np.pi)*(first**2)))
return second
plt.pyplot.figure()
plt.pyplot.plot(l_m, second(a_x, first(l_m, r_x)))
plt.pyplot.xlabel("l_m")
plt.pyplot.ylabel("second")
plt.pyplot.show()
It could very well be that I forgot to copy a part or changed my code too much while choosing which part to ask about. I did find a solution but it's a very clumsy one in which I don't refer to the functions but rather their definitions and thus have absolutely impossible and stupidly looking functions.
How do I find an elegant solution to my problem?
def second(a_x, first):
second = 0.5*((a_x**2)/(0.25*(np.pi)*(first**2)))
return second
plt.pyplot.figure()
plt.pyplot.plot(l_m, second(a_x, first(l_m, r_x)))
In this case you aren't really passing the function as an argument. You're just passing the value returned by the function first() and then squaring it. That's why it works. And if you want to do this then you should rename first as an argument in second so you know it's not the function you're referring to.
If for whatever reason you do want to pass the function and call it within second, you can try
def second(a_x, first, l_m, r_x):
second = 0.5*((a_x**2)/(0.25*(np.pi)*(first(l_m, r_x)**2)))
return second
plt.pyplot.figure()
plt.pyplot.plot(l_m, second(a_x, first, l_m, r_x))
The difference here is that first() is actually being called inside second instead of being called outside and sending it's value
I have a script which accepts, on the command line, arguments to create a matplotlib.patches.Rectangle object.
Typical arguments that could be passed to the Rectangle constructor might be something like (in Python, without argument parsing):
patches.Rectangle(
xy=(15000,0),
width=9.4,
height=4,
color='peachpuff'
)
The user can encode this on the command line like so:
--patches '[{ "x" : 15000,"y" : 0,"width" : 9.4,"height" : 4,"color" : "peachpuff"}]'
This json array is loaded with json.loads. Note that most of the arguments can just be passed directly through to the Rectangle constructor, but the xy tuple causes a problem: json.loads will never generate a tuple type, so you can't create a tuple this way.
To work around it, I have the user pass separate x and y mappings, and then combine them like this:
p.add_argument('--patches', help='A JSON array of patches.Rectangle object arguments to patch onto the chart',
type=json.loads)
# stuff
# put some patches on
if args.patches:
def from_json(json):
ret = dict(json)
del ret['x']
del ret['y']
ret['xy'] = (json['x'], json['y'])
return ret
for pjson in args.patches:
p = from_json(pjson)
ax.add_patch(patches.Rectangle(**p))
Half of that code (essentially all of from_json) is just dealing with the transformation of x and y args into the xy = (x, y) tuple.
Any more elegant/Pythonic way to deal with this?
It might involve cleaner handling of the json input, or perhaps passing something other than json on the command line (but it must have rough feature-parity with the current approach).
Your from_json function can effectively be used as a custom type for your --patches option.
def RectangleArgs(s):
d = json.loads(s)
d['xy'] = (d.pop('x'), d.pop('y'))
return d
...
parser.add_argument('--patches', action='append', type=RectangleArgs)
...
for patch in args.patches:
ax.add_patch(patches.Rectangle(**patch))
Aside from simply massaging the JSON read from the command line, you can do additional validation, raising argparse.ArgumentTypeError if you find anything that would cause a problem when you try to use the return value in a call to patches.Rectangle.
Your motivating assumption that you need a tuple for the xy argument to Rectangle is not correct: a 2-element list works just as well, so "xy" : "[1, 2]" will work fine.
Pointed out by Jeff in the comments.
I'm trying to use the ast module in Python to parse input code, but am struggling with a lot of the syntax of how to do so. For instance, I have the following code as a testing environment:
import ast
class NodeVisitor(ast.NodeVisitor):
def visit_Call(self, node):
for each in node.args:
print(ast.literal_eval(each))
self.generic_visit(node)
line = "circuit = QubitCircuit(3, True)"
tree = ast.parse(line)
print("VISITOR")
visitor = NodeVisitor()
visitor.visit(tree)
Output:
VISITOR
3
True
In this instance, and please correct me if I'm wrong, the visit_Call will be used if it's a function call? So I can get each argument, however there's no guarantee it will work like this as there are different arguments available to be provided. I understand that node.args is providing my arguments, but I'm not sure how to do things with them?
I guess what I'm asking is how do I check what the arguments are and do different things with them? I'd like to check, perhaps, that the first argument is an Int, and if so, run processInt(parameter) as an example.
The value each in your loop in the method will be assigned to the AST node for each of the arguments in each function call you visit. There are lots of different types of AST nodes, so by checking which kind you have, you may be able to learn things about the argument being passed in.
Note however that the AST is about syntax, not values. So if the function call was foo(bar), it's just going to tell you that the argument is a variable named bar, not what the value of that variable is (which it does not know). If the function call was foo(bar(baz)), it's going to show you that the argument is another function call. If you only need to handle calls with literals as their arguments, then you're probably going to be OK, you'll just look instances of AST.Num and similar.
If you want to check if the first argument is a number and process it if it is, you can do something like:
def visit_Call(self, node):
first_arg = node.args[0]
if isinstance(first_arg, ast.Num):
processInt(first_arg.n)
else:
pass # Do you want to do something on a bad argument? Raise an exception maybe?
In the following simple matplotlib code:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0,5,0.1)
y = np.sin(1.33*x)
x1, y1 = np.meshgrid(x, y)
data = np.sin(x1) + y1**4
im = plt.imshow(data)
x = im.make_image()
...
I get the following inexplicable error in the last statement:
"TypeError: make_image() takes at least 2 arguments (1 given)"
And I get an even more ridiculous error if I use an argument, e.g.
x = im.make_image(magnification=2.0)
"TypeError: make_image() takes at least 2 arguments (2 given)".
This is one of the most ridilulous programming errors I have ever come upon!
I have found the missing ingredient: its a renderer. E.g.
r = plt.gcf().canvas.get_renderer()
x = im.make_image(r, magnification=2.0)
This works. Meanwhile, however, I found out with the help of a commentator here that this make_image function is not of any real use, and it is not much supported. Image maginifcation must be obtained with other means, e.g. axes.
So I consider the question solved. Thank you.
See e.g. this question for why something like
TypeError: method() takes at least n arguments (n given)
is not as ridiculous as it may sound at first sight.
Here you are calling make_image without any positional argument. The signature, however, is
make_image(renderer, magnification=1.0, unsampled=False)
So you are missing the renderer argument.
In python 3.6 the error is a little more clear. It would say something like
TypeError: make_image() missing 1 required positional argument: 'renderer'
which allows to find out the problem more easily.
Apart the question stays unclear on what the desired outcome is, so that's about what one can say at this point.
I am new to Python. I discover that in the arguments of e.g., annotate, it is allow to put xy=..., xytext=...., is it a feature of Python? If yes how do we define a function in python that allow this?
import numpy as np
from matplotlib import pyplot as plt
plt.annotate('Here is something special', xy = (2, 1), xytext=(1,5),arrowprops={'facecolor': 'r'})
This is a python's feature, called keyword parameters.
Functions can also be called using keyword arguments of the form "keyword = value". You can read about this topic in the documentation.
keyword arguments is not different than normal arguments except order isn't important. There is nothing special to do in the definition. For example:
def my_func(a, b):
pass
my_func(1, 2)
my_func(b=2, a=1)
# Both of them get the same results
I'm not sure if I completely understand what you want to do, but I think that this part of the Python documentation might answer your question:
https://docs.python.org/release/1.5.1p1/tut/keywordArgs.html