파이썬 함수에 대한 사용자 입력으로 문자열이 주어지면 현재 정의 된 네임 스페이스에 해당 이름의 클래스가있는 경우 클래스 객체를 가져 오려고합니다. 본질적으로 이러한 종류의 결과를 생성하는 함수에 대한 구현을 원합니다.
class Foo:
pass
str_to_class("Foo")
==> <class __main__.Foo at 0x69ba0>
이것이 가능합니까?
파이썬 함수에 대한 사용자 입력으로 문자열이 주어지면 현재 정의 된 네임 스페이스에 해당 이름의 클래스가있는 경우 클래스 객체를 가져 오려고합니다. 본질적으로 이러한 종류의 결과를 생성하는 함수에 대한 구현을 원합니다.
class Foo:
pass
str_to_class("Foo")
==> <class __main__.Foo at 0x69ba0>
이것이 가능합니까?
답변:
경고 :
eval()
임의의 파이썬 코드를 실행하는 데 사용할 수 있습니다. 신뢰할 수없는 문자열과 함께 사용 해서는 안됩니다eval()
. ( 신뢰할 수없는 문자열에 대한 Python eval ()의 보안? )
가장 간단한 것 같습니다.
>>> class Foo(object):
... pass
...
>>> eval("Foo")
<class '__main__.Foo'>
이것은 작동 할 수 있습니다 :
import sys
def str_to_class(classname):
return getattr(sys.modules[__name__], classname)
def str_to_class(str): return vars()[str]
?
당신은 다음과 같은 것을 할 수 있습니다 :
globals()[class_name]
globals()
피해야 할 또 다른 것을 사용하고 있습니다
globals()
보다 훨씬 나쁘지는 eval
않지만 실제로 sys.modules[__name__]
AFAIK 보다 나쁘지는 않습니다 .
Baz
모듈에 있는 클래스 가 필요합니다 foo.bar
. Python 2.7에서는을 사용하려고 importlib.import_module()
합니다. Python 3으로 쉽게 전환 할 수 있습니다.
import importlib
def class_for_name(module_name, class_name):
# load the module, will raise ImportError if module cannot be loaded
m = importlib.import_module(module_name)
# get the class, will raise AttributeError if class cannot be found
c = getattr(m, class_name)
return c
파이썬 <2.7에서 :
def class_for_name(module_name, class_name):
# load the module, will raise ImportError if module cannot be loaded
m = __import__(module_name, globals(), locals(), class_name)
# get the class, will raise AttributeError if class cannot be found
c = getattr(m, class_name)
return c
사용하다:
loaded_class = class_for_name('foo.bar', 'Baz')
import sys
import types
def str_to_class(field):
try:
identifier = getattr(sys.modules[__name__], field)
except AttributeError:
raise NameError("%s doesn't exist." % field)
if isinstance(identifier, (types.ClassType, types.TypeType)):
return identifier
raise TypeError("%s is not a class." % field)
이것은 구식과 신식 수업을 정확하게 처리합니다.
django가 이것을 처리하는 방법을 보았습니다.
django.utils.module_loading이 가지고 있습니다
def import_string(dotted_path):
"""
Import a dotted module path and return the attribute/class designated by the
last name in the path. Raise ImportError if the import failed.
"""
try:
module_path, class_name = dotted_path.rsplit('.', 1)
except ValueError:
msg = "%s doesn't look like a module path" % dotted_path
six.reraise(ImportError, ImportError(msg), sys.exc_info()[2])
module = import_module(module_path)
try:
return getattr(module, class_name)
except AttributeError:
msg = 'Module "%s" does not define a "%s" attribute/class' % (
module_path, class_name)
six.reraise(ImportError, ImportError(msg), sys.exc_info()[2])
당신은 그것을 사용할 수 있습니다 import_string("module_path.to.all.the.way.to.your_class")
예, 당신은 이것을 할 수 있습니다. 클래스가 전역 네임 스페이스에 있다고 가정하면 다음과 같이됩니다.
import types
class Foo:
pass
def str_to_class(s):
if s in globals() and isinstance(globals()[s], types.ClassType):
return globals()[s]
return None
str_to_class('Foo')
==> <class __main__.Foo at 0x340808cc>
임의 코드 실행 또는 원하지 않는 사용자 전달 이름의 관점에서 허용 가능한 함수 / 클래스 이름 목록이있을 수 있으며 입력이 목록의 이름과 일치하면 평가됩니다.
추신 : 나는 알고 있습니다 .... 킨다 늦었지만 .... 앞으로이 문제를 우연히 발견하는 다른 사람들을위한 것입니다.
importlib을 사용 하면 가장 효과적이었습니다.
import importlib
importlib.import_module('accounting.views')
가져 오려는 파이썬 모듈에 문자열 점 표기법 을 사용 합니다 .
문자열로 만든 클래스를 실제로 검색하려면 사전에 클래스를 저장하거나 올바르게 참조 해야합니다. 결국 클래스의 이름을 더 높이고 원하지 않는 클래스를 노출시키지 않아도됩니다.
예를 들어, 파이썬에서 액터 클래스가 정의되어 있고 사용자 입력으로 다른 일반 클래스에 도달하지 않으려는 게임에서.
다른 예 (아래 예와 같이)는 위의 내용을 포함하는 완전히 새로운 클래스를 만드는 것 dict
입니다. 이것은 :
예:
class ClassHolder(object):
def __init__(self):
self.classes = {}
def add_class(self, c):
self.classes[c.__name__] = c
def __getitem__(self, n):
return self.classes[n]
class Foo(object):
def __init__(self):
self.a = 0
def bar(self):
return self.a + 1
class Spam(Foo):
def __init__(self):
self.a = 2
def bar(self):
return self.a + 4
class SomethingDifferent(object):
def __init__(self):
self.a = "Hello"
def add_world(self):
self.a += " World"
def add_word(self, w):
self.a += " " + w
def finish(self):
self.a += "!"
return self.a
aclasses = ClassHolder()
dclasses = ClassHolder()
aclasses.add_class(Foo)
aclasses.add_class(Spam)
dclasses.add_class(SomethingDifferent)
print aclasses
print dclasses
print "======="
print "o"
print aclasses["Foo"]
print aclasses["Spam"]
print "o"
print dclasses["SomethingDifferent"]
print "======="
g = dclasses["SomethingDifferent"]()
g.add_world()
print g.finish()
print "======="
s = []
s.append(aclasses["Foo"]())
s.append(aclasses["Spam"]())
for a in s:
print a.a
print a.bar()
print "--"
print "Done experiment!"
이것은 나를 돌려줍니다 :
<__main__.ClassHolder object at 0x02D9EEF0>
<__main__.ClassHolder object at 0x02D9EF30>
=======
o
<class '__main__.Foo'>
<class '__main__.Spam'>
o
<class '__main__.SomethingDifferent'>
=======
Hello World!
=======
0
1
--
2
6
--
Done experiment!
그와 관련된 또 다른 재미있는 실험은 피클 링 방법을 추가하여 수행 한 ClassHolder
모든 수업을 잃지 않도록하는 것입니다 : ^)