Dynamic languages allow dispatching with and invoking on values from variables whose values are only known at run-time. Contrasting examples in Perl:
class names
constant
Foo::Bar->some_method Foo::Bar::->some_method 'Foo::Bar'->some_method
These are all identical, except the first one is an edge case. If there's a subroutine defined in scope with that name, the dispatch happens on its return value, which leads to difficult to understand bugs. The quoted versions are always safe.
dynamic
my $class_name = 'Foo::Bar'; $class_name->some_method
method names
constant
Some::Class->foo_bar
dynamic
my $method_name = 'foo_bar'; Some::Class->$method_name
function names
constant
foo_bar; (\&foo_bar)->()
dynamic
my $function_name = 'foo_bar'; (\&$function_name)->()
I wonder, how do languages whose variable names have no sigils (normally, or at all) deal with these problems, specifically how did their language designers disambiguate the following?
- resolving class name
FooBar.some_method
where classFooBar
might be name literal or a variable whose value is a class name - dispatching to
SomeClass.foo_bar
where methodfoo_bar
might be name literal or a variable whose value is a method name - invoking
foo_bar
where the function might be a name literal or a variable whose value is a function
I'm primarily interested in the three languages mentioned in this question's tags, but if you know a different dynamic language with no sigils, you can answer too.
Python does not allow you to treat objects the same as strings containing the names of variables referring to those objects. If
obj
is a variable whose value is an object, you can doFooBar.some_method()
. If you have a string"FooBar"
, you have to do something else entirely. Exactly what you do depends on where you expect to find that variable"FooBar"
(i.e., is it a global variable, a local variable, an attribute name, or what). You must look up the name in whatever namespace you think it should be in, and then perform your operation on the resulting object. For instance, if you want to interpret"FooBar"
as a global variable, you can doglobals()["FooBar"].some_method()
.The situation is the same for functions, since functions in Python are just objects like any other. If you have a string
"foo_bar"
that you think refers to a function namedfoo_bar
in the global namespace, you can doglobals()["foo_bar"]()
to try to call it.For methods, the situation is basically the same, except that for methods you always know what namespace you're trying to look up the method name in: it's the namespace of the object you're trying to call the method on. For this you use the
getattr
function. If you have a string"method_name"
and want to call the method of that name onFooBar
, you dogetattr(FooBar, "method_name")()
.The
getattr
approach can also be used to look up global names in another module's namespace. If you thinkfunction_name
refers to a function in another module's global namespace, you can dogetattr(other_module, function_name)
.Here are some examples:
In short, there is no ambiguity, because Python does not allow you to use the same syntax to try to do things with objects and with strings referring to objects. Trying to do something like
"obj".method()
directly is unambiguous in Python:"obj"
is a string, so it can only mean you are trying to call the method on the string itself. There is no attempt to implicitly "decode" the string to see if it happens to contain a variable name. Also, there is no conceptual difference between the operations for looking up a class, function, or method, because those are all first-class objects in Python. The procedure is always the same: first, get the namespace where you want to look up the name; then, look it up. Both steps have to be explicit.It's also worth noting that using this sort of string-based lookup in
globals()
is usually considered hackish in Python. Use ofgetattr
and the like is considered okay only when you're dealing with a highly dynamic data structure (e.g., reading user data from some kind of self-documenting file that tells you what its own fields are called) or some kind of metaprogramming (e.g., a plugin framework). Using something likeglobals()["some_class"]()
for such simple cases would be considered very unpythonic.