【发布时间】:2020-12-23 15:40:46
【问题描述】:
我创建了一个元类,它在继承类的__init__ 函数参数中添加 args 和 kwargs,然后为初始化继承类实例增压它们
示例:
class A():
def __init__(self, a:int, taunt = None):
#print('init a')
self.a = a
self.test = None
class B(A, metaclass=MagicMeta):
def __init__(self, b:int):
#print('init b')
self.b = b
class Foo(B,metaclass=MagicMeta):
def __init__(self,yolo, name ='empty', surname = None):
self.name = name
self.surname= surname
#print(self.test)
def __str__(self):
return str(self.__class__) + ": " + str(self.__dict__)
x =Foo(yolo=1,a=2,b=3, name='name!')
print(x.a)
print(x.b)
print(x.name)
print(str(x))
print(inspect.getsourcelines(A.__init__))
inspect.getsourcelines(Foo.__init__)
> 2
> 3
> name!
> "<class '__main__.Foo'>: {}"
> ([' def __init__(self, a:int, taunt = None):\n', " print('init a')\n", ' self.a = a\n', ' self.test = None\n'], 2)
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
in
4 print(x.name)
5 print(str(x))
----> 6 inspect.getsourcelines(Foo.__init__)
~/opt/anaconda3/lib/python3.8/inspect.py in getsourcelines(object)
965 raised if the source code cannot be retrieved."""
966 object = unwrap(object)
--> 967 lines, lnum = findsource(object)
968
969 if istraceback(object):
~/opt/anaconda3/lib/python3.8/inspect.py in findsource(object)
796 lines = linecache.getlines(file)
797 if not lines:
--> 798 raise OSError('could not get source code')
799
800 if ismodule(object):
OSError: could not get source code
第一个问题,Foo实例的self不能为空,修改__init__Foo类函数的代码后,我不能再读了
这里是 MagicMeta 代码:
import re
from inspect import Parameter
# get arg and kwargs of a function
def get_args(f):
args = list()
kwargs = dict()
for param in inspect.signature(f).parameters.values():
if (param.kind == param.POSITIONAL_OR_KEYWORD):
if param.default ==Parameter.empty:
args.append(param.name)
else:
kwargs[param.name]= param.default
return args, kwargs
def compileKwargs(dct):
string =""
poke = False
for k, o in dct.items():
if type(o) == str:
string+= k+"='"+o+"', "
else:
string+= k+"="+str(o)+", "
return string
def stringArgs(liste):
return " ".join([e+"," for e in liste])
def compileArgs(liste1,liste2):
liste1.extend([e for e in liste2 if e not in liste1])
return liste1
def editFuncName(actual: str, replace:str):
#print('EDITFUNCNAME')
#print(actual)
string = re.sub('(?<=def ).*?(?=\()',replace, actual)
#print('string', string)
return string
import inspect
from textwrap import dedent, indent
# indent the string code
def processCode(code : list):
string=""
#print('processcode')
for i,e in enumerate(code):
#print('row', e)
#print('dedent', e)
if i != 0:
string+=indent(dedent(e),'\t')
else :
string+=dedent(e)
return string
import types
class MagicMeta(type):
def __init__(cls, name, bases, dct):
setattr(cls,'_CODE_', dict())
func = cls.__init__
cls._CODE_[func.__name__]= inspect.getsourcelines(func)
args2 =get_args(cls.__bases__[0].__init__)
setattr(cls,'_ARGS_', dict())
cls._ARGS_[func.__name__]=[get_args(func), args2]
lines = cls._CODE_['__init__']
string= lines[0][0]
arg, kwarg = cls._ARGS_['__init__'][0]
arg2, kwarg2 = cls._ARGS_['__init__'][1]
comparg = stringArgs(compileArgs(arg, arg2))
dct = {**kwarg ,**kwarg2}
#print(dct)
newargs = comparg + compileKwargs(dct)
string = re.sub('(?<=\().*?(?=\))',newargs, string)
superarg =stringArgs(arg2) + compileKwargs(kwarg2)
#print(superarg)
superx = "super({},self).{}({})\n".format(cls.__name__, func.__name__, superarg)
code = lines[0]
#print('LINE DEF', code[0])
code[0]= editFuncName(string, 'tempo')
code.insert(1, superx)
#print('code:',code)
codestr = processCode(code)
#print('précompile', codestr)
comp = compile(codestr, '<string>','exec')
#print(comp)
#exec the code to define the 'tempo' function which will replace __init__
exec(comp)
cls.__init__ = types.MethodType(eval('tempo'), cls)
#print(eval('tempo.__code__'))
【问题讨论】:
-
这可能是 XY 问题。即使您解决了当前的问题,我也可以在代码中看到其他(可能的)问题。例如,我希望您添加的
super()是错误的。您正在创建的 init 函数通过在生成的 init 函数范围内不可用的名称引用一个类。您想在这里实现什么目标? -
我尝试从继承的类中传递 arg,这允许我从继承的最后一个类中初始化继承的类实例,这就像 go with structure 中的继承。当我要更新一个类时,每个继承这个类的类都可以在不改变每个类的代码的情况下初始化它
标签: python python-3.x inheritance metaclass