First of, let me say that I absolutely agree with your opening statement: I find that with every new language I learn, as long as it is at least somewhat viable and a little different from those I used before, it makes me a better programmer in all the languages I already know, because I get to think about problems in terms of the sum of all these languages and be more reflective and realistic about their strengths and weaknesses. A little programmer Sapir-Whorf going on there maybe?
As for your Python example of instanciating a class by a string - well. I realize this is going to be the groan-I-knew-someboy-was-going-to-do-it response, but I can't resist listing some imho not-so-hard ways to do this anyway:
- If you know the namespace the class is in, say your locals or globals, you can instanciate it by indexing the dictionaries returned by globals() or locals() respectively, say: globals()['classname'](constructor arguments)
- If the class is in a module, it's an attribute of the module object. If your class is nested in another class (or class instance), it's an attribute of the class (or one of its instances). Then you can do an attribute fetch by name string using the built-in getattr() function: getattr(module or any other object, 'classname')(constructor args)
- If two ways of doing this already sounds like too much, there's vars(): Without an argument it's identical to locals(), with an object as argument it returns that object's naemspace dictionary, which you can then index: vars(nothing or module or any other object)['classname'](constructor args)
- Of course, since classes are just objects, you can also add them to a container, and then access them that way. I.e. you can do: adict = { aclass.__name__ = aclass } and then adict['aclass'](constructor args)
IMHO, these examples demonstrate the same sort of "in line with the way everything else works in Python" property: Objects have namespace dictionaries that store their attributes. Attributes reference objects. Classes are objects. Modules are objects. Dictionaries are objects and reference objects.
And this sort of consistency is something Python is, for the most part, really really good at. Cf. the simple rules for assignment consistently popping up everywhere: Your regular assignment operation, function argument passing, loop variable assignment, and so on. And since assignment in Python can do impressive things (say, iterable unpacking: a, b, c = [1, 2, 3] - even nested, very handy to unpack lists of lists in a loop) the fact that it's used postively everywhere brings great power, from simple rules. That's what I meant.
No, truth be told, Python is not without its warts either. The idiotic ordering in Python 2.x I mentioned earlier is one such example (thankfully now fixed in version 3 of the language, along with a slew of others). The somewhat ugly exception of classes that use "slots" to store their attributes rather than namespace dictionaries are another (still around even in v3, though thankfully used only rarely, and built-ins like getattr() and vars() work the same on them and their instances regardless) are another. But compared to PHP ...
As for your Python example of instanciating a class by a string - well. I realize this is going to be the groan-I-knew-someboy-was-going-to-do-it response, but I can't resist listing some imho not-so-hard ways to do this anyway:
- If you know the namespace the class is in, say your locals or globals, you can instanciate it by indexing the dictionaries returned by globals() or locals() respectively, say: globals()['classname'](constructor arguments)
- If the class is in a module, it's an attribute of the module object. If your class is nested in another class (or class instance), it's an attribute of the class (or one of its instances). Then you can do an attribute fetch by name string using the built-in getattr() function: getattr(module or any other object, 'classname')(constructor args)
- If two ways of doing this already sounds like too much, there's vars(): Without an argument it's identical to locals(), with an object as argument it returns that object's naemspace dictionary, which you can then index: vars(nothing or module or any other object)['classname'](constructor args)
- Of course, since classes are just objects, you can also add them to a container, and then access them that way. I.e. you can do: adict = { aclass.__name__ = aclass } and then adict['aclass'](constructor args)
IMHO, these examples demonstrate the same sort of "in line with the way everything else works in Python" property: Objects have namespace dictionaries that store their attributes. Attributes reference objects. Classes are objects. Modules are objects. Dictionaries are objects and reference objects.
And this sort of consistency is something Python is, for the most part, really really good at. Cf. the simple rules for assignment consistently popping up everywhere: Your regular assignment operation, function argument passing, loop variable assignment, and so on. And since assignment in Python can do impressive things (say, iterable unpacking: a, b, c = [1, 2, 3] - even nested, very handy to unpack lists of lists in a loop) the fact that it's used postively everywhere brings great power, from simple rules. That's what I meant.
No, truth be told, Python is not without its warts either. The idiotic ordering in Python 2.x I mentioned earlier is one such example (thankfully now fixed in version 3 of the language, along with a slew of others). The somewhat ugly exception of classes that use "slots" to store their attributes rather than namespace dictionaries are another (still around even in v3, though thankfully used only rarely, and built-ins like getattr() and vars() work the same on them and their instances regardless) are another. But compared to PHP ...