-
-
Save bazzaar/d1436636613f1f1d7e0157df204f98ec to your computer and use it in GitHub Desktop.
use Inline::Python; | |
my $py = Inline::Python.new(); | |
$py.run('import matplotlib.pyplot'); | |
class Matplotlib::Plot { | |
method cm { | |
class { | |
# this getattr method, called like so : 'say $plt.cm.getattr($cmap, 'N');' | |
method getattr($obj, $name) { | |
$py.call('__builtin__', 'getattr', $obj, $name); | |
} | |
method FALLBACK($name, $idx) { | |
$py.run("matplotlib.pyplot.cm.{$name}($idx)", :eval); | |
} | |
}.new(); | |
} | |
method FALLBACK($name, |c) { | |
$py.call('matplotlib.pyplot', $name, |c) | |
} | |
} | |
class Matplotlib { | |
method FALLBACK($name, |c) { | |
$py.call('matplotlib', $name, |c) | |
} | |
} |
use v6; | |
use lib '.'; | |
use Matplotlib_reduced; | |
my $plt = Matplotlib::Plot.new; | |
my $cmap = $plt.get_cmap('seismic', 5); | |
# -- this works | |
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N'); | |
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N'); | |
# -- this fails on the 3rd call | |
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N'); | |
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N'); | |
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N'); | |
# -- this works | |
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name'); | |
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name'); | |
# -- this fails on the 3rd call | |
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name'); | |
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name'); | |
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name'); | |
# -- this works | |
#say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N'); | |
#say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name'); | |
# -- this fails on the 3rd call | |
say 'Colormap Name : ' ~ $plt.cm.getattr($cmap, 'name'); | |
say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N'); | |
say 'Number of Colors : ' ~ $plt.cm.getattr($cmap, 'N'); | |
# -- etc.... | |
Here's one more option... Attributes are not methods, you don't call them... you just ask what they are... sometimes you can assign to them (if Python does not define them as a @property
). So in this way, they are kind of like keys of a hash... so you can treat them as such.
class {
my $obj = $py.call( ... );
method AT-KEY($name) {
$obj.__getattribute__($name);
}
method ASSIGN-KEY($name, $new-value) {
$obj.__setattr__($name, $new-value)
}
method FALLBACK($name, |c) { ... }
}
Now you could access (and assign to) attributes as if they were a Hash
$pyobject<attributename> = "new";
# calls $pyobject.ASSIGN-KEY('attributename', 'new')...
# which calls $pyobject.__setattr('attributename', 'new')
say $pyobject<attributename>; # new
Upon further experimentation, it seems like problems happen as soon as you start doing calls to methods on __builtin__
. If I avoid those methods, I don't seem to hit the same problem.
For example, in my FALLBACK
in the earlier comment, if I don't check __builtin__.hasattr
then it works, but of course, Python will throw an exception if the attribute doesn't exists. You can put a try
in front of the call to __getattribute__
and it will return an undefined value (instead of throwing) if the attribute doesn't exists.
I'm not yet sure how to put all these ideas together in a cohesive whole, but play around with them and see what you come up with.
btw python's getattr
supports a third argument which is a default value in case the attribute doesn't exist.
If you write a wrapper, maybe the simplest thing to do is to create a sentinel value Any.new
as the default, and if that is returned, interpret it as not found.
More thanks 0racle, and Moritz, I definitely got way more out of this than I put in :-) I hope this thread has somehow kickstarted a future new article on your Perl6/Matplotlib blog 0racle. I agree about not bugging the Inline::Python maintainer over this when there is a work around.
It all seems so straightforward when it is explained, like you have both done in your comments, I'm not sure after a month of researching I'd have come to the same understanding :-) It's given me a whole ton of concepts and reasoning to investigate. I'll carry on with my efforts on this, and try to add some results to this gist when I have them. Thanks again.
So... here's an example of how you could wrap it...
I'm using an anonymous class, but you could just as easily define a proper class outside of
Matplotlib::Plot
In any case... this works now
... though it's annoying to wrap every attribute like this... I had hoped this would work...
and it does work... the first time, then it throws an error saying
method-wrapper' object has no attribute
on the second call to any attribute. I dunno, this seems like a bug inInline::Python
, but I don't want to bother the maintainer with issues I can work around with a little effort anyways. He's already put in a massive effort to makeInline::Python
work as well as it does.