Ich möchte in der Lage sein, die __init__
Methode einer Klasse zu fragen, was es Parameter sind. Der einfache Ansatz ist der folgende:Suche nach den Parametern einer Funktion in Python
Allerdings wird das nicht funktionieren, wenn die Klasse Dekoratoren hat. Es gibt die Parameterliste für die Funktion, die vom Decorator zurückgegeben wird. Ich möchte auf die ursprüngliche __init__
Methode zurückgreifen und diese ursprünglichen Parameter erhalten. Im Fall eines Dekorateur, ist die Dekorateur Funktion geht in der Schließung der Funktion durch den Dekorateur zurück gefunden werden:
cls.__init__.__func__.__closure__[0]
Allerdings ist es komplizierter, wenn es andere Dinge in der Schließung ist, der Dekorateure kann von Zeit zu Zeit tun:
def Something(test):
def decorator(func):
def newfunc(self):
stuff = test
return func(self)
return newfunc
return decorator
def test():
class Test(object):
@Something(4)
def something(self):
print Test
return Test
test().something.__func__.__closure__
(<cell at 0xb7ce7584: int object at 0x81b208c>, <cell at 0xb7ce7614: function object at 0xb7ce6994>)
und dann von der ursprünglichen Funktion muss ich entscheiden, ob ich auf die Parameter von Dekorateur oder die Parameter werden soll. Die vom Decorator zurückgegebene Funktion könnte *args
und **kwargs
für ihre Parameter haben. Was ist, wenn es mehrere Dekorateure gibt und ich entscheiden muss, welche mir wichtig ist?
Also, was ist der beste Weg, die Parameter einer Funktion zu finden, selbst wenn die Funktion dekoriert sein sollte? Was ist der beste Weg, um eine Kette von Dekorateuren zurück in die dekorierte Funktion zu bringen?
Update:
Hier ist effektiv, wie ich dieses Recht jetzt tue (Namen geändert wurden, um die Identität des Angeklagten zu schützen):
import abc
import collections
IGNORED_PARAMS = ("self",)
DEFAULT_PARAM_MAPPING = {}
DEFAULT_DEFAULT_PARAMS = {}
class DICT_MAPPING_Placeholder(object):
def __get__(self, obj, type):
DICT_MAPPING = {}
for key in type.PARAMS:
DICT_MAPPING[key] = None
for cls in type.mro():
if "__init__" in cls.__dict__:
cls.DICT_MAPPING = DICT_MAPPING
break
return DICT_MAPPING
class PARAM_MAPPING_Placeholder(object):
def __get__(self, obj, type):
for cls in type.mro():
if "__init__" in cls.__dict__:
cls.PARAM_MAPPING = DEFAULT_PARAM_MAPPING
break
return DEFAULT_PARAM_MAPPING
class DEFAULT_PARAMS_Placeholder(object):
def __get__(self, obj, type):
for cls in type.mro():
if "__init__" in cls.__dict__:
cls.DEFAULT_PARAMS = DEFAULT_DEFAULT_PARAMS
break
return DEFAULT_DEFAULT_PARAMS
class PARAMS_Placeholder(object):
def __get__(self, obj, type):
func = type.__init__.__func__
# unwrap decorators here
code = func.__code__
keys = list(code.co_varnames[:code.co_argcount])
for name in IGNORED_PARAMS:
try: keys.remove(name)
except ValueError: pass
for cls in type.mro():
if "__init__" in cls.__dict__:
cls.PARAMS = tuple(keys)
break
return tuple(keys)
class BaseMeta(abc.ABCMeta):
def __init__(self, name, bases, dict):
super(BaseMeta, self).__init__(name, bases, dict)
if "__init__" not in dict:
return
if "PARAMS" not in dict:
self.PARAMS = PARAMS_Placeholder()
if "DEFAULT_PARAMS" not in dict:
self.DEFAULT_PARAMS = DEFAULT_PARAMS_Placeholder()
if "PARAM_MAPPING" not in dict:
self.PARAM_MAPPING = PARAM_MAPPING_Placeholder()
if "DICT_MAPPING" not in dict:
self.DICT_MAPPING = DICT_MAPPING_Placeholder()
class Base(collections.Mapping):
__metaclass__ = BaseMeta
"""
Dict-like class that uses its __init__ params for default keys.
Override PARAMS, DEFAULT_PARAMS, PARAM_MAPPING, and DICT_MAPPING
in the subclass definition to give non-default behavior.
"""
def __init__(self):
pass
def __nonzero__(self):
"""Handle bool casting instead of __len__."""
return True
def __getitem__(self, key):
action = self.DICT_MAPPING[key]
if action is None:
return getattr(self, key)
try:
return action(self)
except AttributeError:
return getattr(self, action)
def __iter__(self):
return iter(self.DICT_MAPPING)
def __len__(self):
return len(self.DICT_MAPPING)
print Base.PARAMS
#()
print dict(Base())
# {}
An dieser Stelle Basis Berichten Werte für uninteressant Die vier contants und die dict-Version der Instanzen sind leer. Wenn Sie jedoch eine Unterklasse Sie eine der vier außer Kraft setzen können, oder Sie können andere Parameter der __init__
umfassen:
class Sub1(Base):
def __init__(self, one, two):
super(Sub1, self).__init__()
self.one = one
self.two = two
Sub1.PARAMS
# ("one", "two")
dict(Sub1(1,2))
# {"one": 1, "two": 2}
class Sub2(Base):
PARAMS = ("first", "second")
def __init__(self, one, two):
super(Sub2, self).__init__()
self.first = one
self.second = two
Sub2.PARAMS
# ("first", "second")
dict(Sub2(1,2))
# {"first": 1, "second": 2}
Warum zum Teufel würden Sie das wollen? – delnan
Die Tatsache, dass dies so schwierig ist, sollte Ihnen zeigen, dass es nicht das Richtige ist. – katrielalex
Ich möchte in der Lage sein, Objekte einer Klasse als Dicts zu verwenden und explizite Steuerelemente darauf haben, welche Schlüssel durch '__getitem__' und' __iter__' exponiert werden. Die Parameter von "__init__" machen große Standard-Keys, also ziehe ich diese programmatisch. Ich versuche nur, Ecken zu adressieren, etwa wenn Deskriptoren involviert werden. –