クラスの作成時に、必ずしも getter/setter メソッドを作成する必要はありません。必要に応じて呼び出し可能オブジェクトを作成することもできます:
class MyClass(object):
# get/set properties for this class: {'Name':length}
__properties = {'LoadFee':20, 'Currency':3}
def __init__(self):
self._Currency = '01 34'
self._LoadFee = 'lorem ipsum dolor sit amet consecuti'
def __getattr__(self, name):
basename = name[3:]
attrname = '_'+basename
if basename in self.__properties:
if name.startswith('Get'):
return lambda : getattr(self, attrname)[:self.__properties[basename]].strip()
elif name.startswith('Set'):
return lambda value: setattr(self, attrname, value)
raise AttributeError(name)
m = MyClass()
print m.GetCurrency()
print m.GetLoadFee()
このアプローチは理解しやすく、ブードゥー教のメタプログラミングを一切使用していませんが、遅く、イントロスペクションを無効にします。
メソッドを呼び出すときにメソッドを「具体化」することで、これを高速化できます。つまり、instancemethod
クラスのインスタンスの属性がアクセスされるときにクラスに をアタッチします。
# MethodType is not actually necessary because
# everything it does can be done with normal Python
# but it will make our dynamic methods look as "normal"
# and not-dynamic as possible to introspection
from types import MethodType
class MyClass(object):
# get/set properties for this class: {'Name':length}
__properties = {'LoadFee':20, 'Currency':3}
def __init__(self, **args):
props = self.__properties
emptystr = ''
for k in props:
setattr(self, '_'+k, args.get(k, emptystr))
def __getattr__(self, name):
print '__getattr__(%s)' % name
# we can cache accesses by "reifying" our getters/setters as they are accessed
cls = self.__class__
basename = name[3:]
attrname = '_'+basename
# nested lambdas are just for delayed evaluation
# they cache property size lookup--assumes __properties is class-constant!
def getsize():
return cls.__properties[basename]
methodfactories = {
'Get': lambda size: lambda self: getattr(self, attrname)[:size].strip(),
'Set': lambda size: lambda self, value: setattr(self, attrname, value),
}
try:
print ' creating', name
callable = methodfactories[name[:3]](getsize())
except (KeyError, AttributeError) as e:
raise AttributeError("'{}' object has no attribute '{}'".format(cls.__name__, name))
callable.__name__ = name #cosmetics
unboundmethod = MethodType(callable, None, cls)
setattr(cls, name, unboundmethod) # add unbound method to the class
# magically get bound method on the instance!
# this works because MethodType creates a descriptor that
# returns a bound callable in an instance context
# and an unbound one in a class context
return getattr(self, name) # not an infinite loop!
次に、次のコードを実行すると:
m = MyClass(Currency='01', LoadFee='lorem ipsum dolor sit')
n = MyClass(Currency='02', LoadFee='amet consecuti')
try:
# AttributeError because it hasn't been used by an instance
MyClass.GetCurrency
except AttributeError, e:
print ' 7:', e
print ' 8:', m.GetCurrency()
print ' 9:', MyClass.GetCurrency
print '10:', m.GetCurrency
print '11:', n.GetCurrency
print '12:', m.GetCurrency is n.GetCurrency
print '13:', n.GetCurrency()
print '14:', m.GetLoadFee()
print '15:', m.__dict__ # no per-instance callable!
次の結果が得られます。
7: type object 'MyClass' has no attribute 'GetCurrency'
8: __getattr__(GetCurrency)
creating GetCurrency
01
9: <unbound method MyClass.GetCurrency>
10: <bound method MyClass.GetCurrency of <__main__.MyClass object at 0x106f87b90>>
11: <bound method MyClass.GetCurrency of <__main__.MyClass object at 0x106f87f10>>
12: False
13: 02
14: __getattr__(GetLoadFee)
creating GetLoadFee
lorem ipsum dolor si
15: {'_Currency': '01', '_LoadFee': 'lorem ipsum dolor sit'}
getattrは、インスタンスが特別なプロパティに最初にアクセスしたときにのみ呼び出されることに注意してください。その後、バインドされたメソッドが動的に作成され、インスタンスのクラスにアタッチされて返されます。属性に最初にアクセスした後、クラスとインスタンスは、「通常の」方法で作成したメソッドとほとんど区別がつかなくなり、実行速度もまったく同じになります。instancemethod