The terrible secrets of dynamic Python.
Strict Standards: call_user_func_array() expects parameter 1 to be a valid callback, non-static method SimilarPosts::execute() should not be called statically in /home/eykd/webapps/net_eykd_newstalk__wp/wp-content/plugins/post-plugin-library/common_functions.php on line 601
Strict Standards: Non-static method SimilarPosts::check_post_plugin_library() should not be called statically in /home/eykd/webapps/net_eykd_newstalk__wp/wp-content/plugins/similar-posts/similar-posts.php on line 57
Strict Standards: call_user_func_array() expects parameter 1 to be a valid callback, non-static method InSeriesInternal::in_series_linker_content_filter() should not be called statically in /home/eykd/webapps/net_eykd_newstalk__wp/wp-includes/plugin.php on line 163
Strict Standards: Non-static method InSeriesInternal::get_the_ID() should not be called statically in /home/eykd/webapps/net_eykd_newstalk__wp/wp-content/plugins/in-series/in-series-internal.php on line 881
Strict Standards: Non-static method InSeries::adv_PostToSeries() should not be called statically in /home/eykd/webapps/net_eykd_newstalk__wp/wp-content/plugins/in-series/in-series-internal.php on line 881
Strict Standards: Non-static method InSeriesInternal::make_int() should not be called statically in /home/eykd/webapps/net_eykd_newstalk__wp/wp-content/plugins/in-series/in-series.php on line 486
Strict Standards: Non-static method InSeriesInternal::PostToSeries() should not be called statically in /home/eykd/webapps/net_eykd_newstalk__wp/wp-content/plugins/in-series/in-series.php on line 490
Strict Standards: Non-static method InSeriesInternal::GetPostSeriesCachedResults() should not be called statically in /home/eykd/webapps/net_eykd_newstalk__wp/wp-content/plugins/in-series/in-series-internal.php on line 323
Strict Standards: Non-static method InSeriesInternal::make_int() should not be called statically in /home/eykd/webapps/net_eykd_newstalk__wp/wp-content/plugins/in-series/in-series-internal.php on line 325
Deprecated: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /home/eykd/webapps/net_eykd_newstalk__wp/wp-content/plugins/wp-slimbox-reloaded/wp-slimbox-reloaded.php on line 84
In honor of the new “geeky” category, I’d like to skim over the joys and terrors of dynamic Python.
In my current programming project, I have in-game characters that need a set of personal attributes defined–you know, things like “strength” and “intelligence”. However, I haven’t settled on a stat system yet, so I wanted to make a flexible architecture which would do all the heavy lifting, and later down the road when I have my system figured out, it would be trivial to implement.
Enter the CharGen class, which is a generic sort of class that keeps track of four different kinds of stats–physical/mental/social “attributes”, “skills”, “meta” attributes, for string-values like names, etc., and “persona”, mainly for numeric values which defy classification. These naturally implemented themselves as dictionaries, so if you want to look at the strength attribute, you just do
If you want to check a character’s skill at driving, you look at
This also makes it easy later to iterate over, say, all the attributes, or over all the skills. Easy, right?
But, what if you want to write a method that needs to check or set an arbitrary stat? Well, first you have to check the stat’s type. Then you have to go to that dictionary and look up the attribute itself. The code looks like this:
attr = 'strength' attrtype = self.all[attr]['type'] getattr(self, attrtype)[attr]
Seems like a lot of work just to check a lousy variable, eh? And, it’s UgLy. I tried stuffing it into a method you could call, and that was going to work okay. But then I thought, “what about when I want to set a stat?” The code is almost the same, using setattr instead of getattr. But it doesn’t allow for any sanity-checking–for instance, if I were using a stat system where strength couldn’t be any higher than 20, I could still set chargen.attr['strength'] = 50, and nobody would complain. A generic method wouldn’t work either, because the CharGen method is meant to be subclassed, and I had no idea what my future self might need as far as checks and balances.
What to do? What to do?
At the same time, I thought, “boy, it sure would be convenient, and nicer to look at, if I could just say
Well, Python has this unassuming little feature called managed properties, where you store the actual data anywhere you want (like in a dictionary, or a private variable, or whatever), then you define a getter and a setter method for that data, and then you just assign chargen.strength to a special property object that knows about the getter and setter, and, voila,
works just like
Cool, huh? And even better, you could set up any sort of rules you want in the setter method, so you can rest assured that nobody will ever be able to set chargen.strength to 50 if you don’t want them to. (They could always use chargen.attr['strength'] to commit whatever foul crimes they like, but I just have to trust my future self not to be so nefarious.)
But I immediately hit another snag: where do the getters and setters come from, if I won’t know the stats and constraints until subclassing time?
The Terrible Secret
Here’s where we start delving into the dark arts of a dynamic language like Python. In a static language, I would be stuck having to do some of the work at subclassing time. Define a stat, then manually define a getter and a setter for it. It’s all very simple code, and it’s very boring to type over and over again. I code because it’s fun, and I avoid coding stuff that’s not fun. This would not be fun.
Then I discovered a recipe for a function factory that makes instance methods. If that didn’t make sense to you, that’s okay, because it’s pretty abstract, and I still don’t quite get it either. In fact, part of me is surprised every time I run the unit test and it passes. Stuff like this shouldn’t work–it’s too subtle, too sleight of hand. And whatever else it may be, it sure ain’t obvious. If I’d read about this two years ago, when I was first learning Python’s syntax, I would only have been able to muster a blank stare. I’ve come a long way.
Now, all my future self has to do is subclass CharGen, define a dictionary of stats with type and constraints, then call CharGen’s init method, and it all happens, dictionaries, properties, getters, setters, everything as if by magic. It’s so cool, it’s creepy.
- None Found