A further reason to avoid metaclasses is that they truly can be avoided. They do not add much power to Python. However, there are cases when they make 'client' code more readable.
Consider this situation: we have a finite set of strings that are used as dictionary keys in several parts of our application. Of course, we don't want to use explicit strings in every point where they are needed. This is too error prone and bugs are more difficult to track.
A good solution is to put all the strings in a module. We will have code such as
FOO1 = 'foo1'
FOO2 = 'foo2'
Good. However, we may want to loop over the constants. One of the more pythonic solutions is this one:
targets = { 'SEARCH': 'search', 'INFO': 'info', 'VARIANTS': 'variants', 'DEPS': 'deps', 'DEPENDENTS': 'dependents', 'INSTALL': 'install','UNINSTALL': 'uninstall', 'ACTIVATE': 'activate', 'DEACTIVATE': 'deactivate', 'INSTALLED':'installed', 'LOCATION': 'location', 'CONTENTS':'contents', 'PROVIDES': 'provides', 'SYNC': 'sync', 'OUTDATED': 'outdated', 'UPGRADE': 'upgrade', 'CLEAN':'clean', 'ECHO': 'echo', 'LIST': 'list', 'VERSION': 'version', 'SELFUPDATE': 'selfupdate', 'HELP': 'help' } locals().update(targets)
In this way we can do whatever we want. However, we could use a class instead of a module. We simply define the constants as class variables. Then we can easily use the locals built-in to create the array of constants. But with a little metaclass trick, we can write more interesting client code.
We could for example write things like:
for k in PackageKey: # do something
or
if key in PackageKey: returnself.foo(key)
We can use metaclasses to define a class object that behaves in the way above.
class_ConstantNamespace(type): def__iter__(cls): for v incls.keys: yield v class PackageKey(object): __metaclass__= _ConstantNamespace NAME ='name' VERSION ='version' REVISION ='revision' DIRECTORY ='directory' VARIANTS ='variants' HOMEPAGE ='homepage' DESCRIPTION ='description' BUILD_DEPENDENCIES ='build_dependencies' LIBRARY_DEPENDENCIES ='library_dependencies' RUNTIME_DEPENDENCIES ='runtime_dependencies' PLATFORMS ='platforms' MAINTAINERS ='maintainers' keys = [locals()[key] for key inlocals().keys() if (key.upper()== key and (key.isalpha()orkey.endswith('_DEPENDENCIES'))]
No comments:
Post a Comment