Let's say I have a class
class MyClass
def sayMyName()
puts "I am unknown"
end
end
and I have stored this method name in a variable: methodName = "saymyName"
I want to call this method by using above variable, something like this:
instance = MyClass.new
instance[methodName]
I know it can be called using a macro but I don't get how? Please someone provide an example with explanation.
Update 1
There is already an answer for this: Calling methods dynamically (Crystal-lang)
but this doesn't answer how to do it when methods are inside a class.
I have adapted the example given in the update:
class Foo
def method1
puts "i'm method1"
end
def method2
puts "i'm method2"
end
def method3
puts "i'm method3"
end
def bar
{ "ctrl": -> { method1 },
"shift": -> { method2 },
"alt": -> { method3 }
}
end
def [](method)
bar[method]
end
end
binding = ["ctrl", "shift", "alt"].sample
foo = Foo.new
foo[binding].call #=> one of them
Working Example
Ruby has this method called the block_given in it so that we can check to see if a block is passed and process accordingly if given. Is there an equivalent method in crystal?
Crystal does not have it for a moment. But you can have similar behavior using method overloading:
def foo
foo {}
end
def foo
yield
end
foo { }
foo
If you absolutely must know whether a block was given, you can do something like this:
def foo(block : (String ->)? = nil)
if block
block.call("Block given")
else
puts "No block given"
end
end
def foo(&block : String ->)
foo(block)
end
foo { |s| puts s }
foo
(For more information on Proc syntax, see https://crystal-lang.org/reference/syntax_and_semantics/type_grammar.html#proc)
Okay, bear with me on this, I know it's going to look horribly convoluted, but please help me understand what's happening.
from functools import partial
class Cage(object):
def __init__(self, animal):
self.animal = animal
def gotimes(do_the_petting):
do_the_petting()
def get_petters():
for animal in ['cow', 'dog', 'cat']:
cage = Cage(animal)
def pet_function():
print "Mary pets the " + cage.animal + "."
yield (animal, partial(gotimes, pet_function))
funs = list(get_petters())
for name, f in funs:
print name + ":",
f()
Gives:
cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.
So basically, why am I not getting three different animals? Isn't the cage 'packaged' into the local scope of the nested function? If not, how does a call to the nested function look up the local variables?
I know that running into these kind of problems usually means one is 'doing it wrong', but I'd like to understand what happens.
The nested function looks up variables from the parent scope when executed, not when defined.
The function body is compiled, and the 'free' variables (not defined in the function itself by assignment), are verified, then bound as closure cells to the function, with the code using an index to reference each cell. pet_function thus has one free variable (cage) which is then referenced via a closure cell, index 0. The closure itself points to the local variable cage in the get_petters function.
When you actually call the function, that closure is then used to look at the value of cage in the surrounding scope at the time you call the function. Here lies the problem. By the time you call your functions, the get_petters function is already done computing it's results. The cage local variable at some point during that execution was assigned each of the 'cow', 'dog', and 'cat' strings, but at the end of the function, cage contains that last value 'cat'. Thus, when you call each of the dynamically returned functions, you get the value 'cat' printed.
The work-around is to not rely on closures. You can use a partial function instead, create a new function scope, or bind the variable as a default value for a keyword parameter.
Partial function example, using functools.partial():
from functools import partial
def pet_function(cage=None):
print "Mary pets the " + cage.animal + "."
yield (animal, partial(gotimes, partial(pet_function, cage=cage)))
Creating a new scope example:
def scoped_cage(cage=None):
def pet_function():
print "Mary pets the " + cage.animal + "."
return pet_function
yield (animal, partial(gotimes, scoped_cage(cage)))
Binding the variable as a default value for a keyword parameter:
def pet_function(cage=cage):
print "Mary pets the " + cage.animal + "."
yield (animal, partial(gotimes, pet_function))
There is no need to define the scoped_cage function in the loop, compilation only takes place once, not on each iteration of the loop.
My understanding is that cage is looked for in the parent function namespace when the yielded pet_function is actually called, not before.
So when you do
funs = list(get_petters())
You generate 3 functions which will find the lastly created cage.
If you replace your last loop with :
for name, f in get_petters():
print name + ":",
f()
You will actually get :
cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.
This stems from the following
for i in range(2):
pass
print(i) # prints 1
after iterating the value of i is lazily stored as its final value.
As a generator the function would work (i.e. printing each value in turn), but when transforming to a list it runs over the generator, hence all calls to cage (cage.animal) return cats.
Let's simplify the question. Define:
def get_petters():
for animal in ['cow', 'dog', 'cat']:
def pet_function():
return "Mary pets the " + animal + "."
yield (animal, pet_function)
Then, just like in the question, we get:
>>> for name, f in list(get_petters()):
... print(name + ":", f())
cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.
But if we avoid creating a list() first:
>>> for name, f in get_petters():
... print(name + ":", f())
cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.
What's going on? Why does this subtle difference completely change our results?
If we look at list(get_petters()), it's clear from the changing memory addresses that we do indeed yield three different functions:
>>> list(get_petters())
[('cow', <function get_petters.<locals>.pet_function at 0x7ff2b988d790>),
('dog', <function get_petters.<locals>.pet_function at 0x7ff2c18f51f0>),
('cat', <function get_petters.<locals>.pet_function at 0x7ff2c14a9f70>)]
However, take a look at the cells that these functions are bound to:
>>> for _, f in list(get_petters()):
... print(f(), f.__closure__)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
>>> for _, f in get_petters():
... print(f(), f.__closure__)
Mary pets the cow. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a95670>,)
Mary pets the dog. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a952f0>,)
Mary pets the cat. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c3f437f0>,)
For both loops, the cell object remains the same throughout the iterations. However, as expected, the specific str it references varies in the second loop. The cell object refers to animal, which is created when get_petters() is called. However, animal changes what str object it refers to as the generator function runs.
In the first loop, during each iteration, we create all the fs, but we only call them after the generator get_petters() is completely exhausted and a list of functions is already created.
In the second loop, during each iteration, we are pausing the get_petters() generator and calling f after each pause. Thus, we end up retrieving the value of animal at that moment in time that the generator function is paused.
As #Claudiu puts in an answer to a similar question:
Three separate functions are created, but they each have the closure of the environment they're defined in - in this case, the global environment (or the outer function's environment if the loop is placed inside another function). This is exactly the problem, though -- in this environment, animal is mutated, and the closures all refer to the same animal.
[Editor note: i has been changed to animal.]
Possible duplicates:
Is there a way to create subclasses on-the-fly?
Dynamically creating classes - Python
I would like to create a subclass where the only difference is some class variable, but I would like everything else to stay the same. I need to do this dynamically, because I only know the class variable value at run time.
Here is some example code so far. I would like FooBase.foo_var to be "default" but FooBar.foo_var to be "bar." No attempt so far has been successful.
class FooBase(object):
foo_var = "default"
def __init__(self, name="anon"):
self.name = name
def speak(self):
print self.name, "reporting for duty"
print "my foovar is '" + FooBase.foo_var + "'"
if __name__ == "__main__":
#FooBase.foo_var = "foo"
f = FooBase()
f.speak()
foobarname = "bar"
#FooBar = type("FooBar", (FooBase,), {'foo_var': "bar"})
class FooBar(FooBase): pass
FooBar.foo_var = "bar"
fb = FooBar()
fb.speak()
Many thanks
EDIT so I obviously have a problem with this line:
print "my foovar is '" + FooBase.foo_var + "'"
The accepted answer has self.foo_var in the code. That's what I should be doing. I feel ridiculous now.
What about this:
def make_class(value):
class Foo(object):
foo_var = value
def speak(self):
print self.foo_var
return Foo
FooBar = make_class("bar")
FooQux = make_class("qux")
FooBar().speak()
FooQux().speak()
That said, can't you make the value of foo_var be a instance variable of your class? So that the same class instantiated with different input behaves in different ways, instead of creating a different class for each of those different behaviours.
Currently I'm trying to create sample Wicket page with Clojure (in existing wicket project). Code looks like this:
(ns a.set.of.packages.dataview.info.EmptyNodeInfo2Panel
(:import [a.set.of.packages.tree TreeModelBean]
[a.set.of.packages.dataview.supplemental GenericHeaderPanel]))
(gen-class
:name a.set.of.packages.dataview.info.EmptyNodeInfo2Panel
:extends org.apache.wicket.markup.html.panel.Panel
:state state
:init init
:constructors {[String a.set.of.packages.tree.TreeModelBean] [String]}
:post-init construct)
(defn -init [id model-bean]
[[id] nil])
(defn -construct [this id model-bean]
(.add this (GenericHeaderPanel. "header" model-bean)))
When page being created I get following runtime error:
java.lang.IllegalArgumentException: No matching method found: add for class a.set.of.packages.dataview.info.EmptyNodeInfo2Panel
i.e. I can't call to the superclass methods.
Decompiled construct method looks like this:
public Object invoke(Object this, Object id, Object model_bean)
throws Exception
{
this = null;
id = null;
model_bean = null;
return Reflector.invokeInstanceMethod(this, "add", new Object[] {
new GenericHeaderPanel((String)"header", (TreeModelBean)model_bean)
});
}
Where everything is set to null! Is this problem with constructor parameters mapping, incorrect decompilation or a bug?
I've dug in Clojure internals and found root of the problem. Marked row doesn't work.
// clojure.lang.Reflector
static public boolean paramArgTypeMatch(Class paramType, Class argType)
{
...
if(paramType == argType || paramType.isAssignableFrom(argType)) // <<<
return true;
...
Probably there is class loader problem (I create EmptyNodeInfo2Panel class dynamically with Class.forName).
Classes that was matched are:
a.set.of.packages.dataview.supplemental.GenericHeaderPanel
org.apache.wicket.Component;
Interesting thing: when I print org.apache.wicket.Component class name from servlet respnose processing thread it prints as is, but when I print it from Reflector thead (through paramType.getName()), it prints in following form: [Lorg.apache.wicket.Component.
I use Tomcat 5.5, are there any clues possible?