programing

문자열을 Python 클래스 개체로 변환하시겠습니까?

bestcode 2022. 9. 18. 12:53
반응형

문자열을 Python 클래스 개체로 변환하시겠습니까?

Python 함수에 대한 사용자 입력으로 문자열을 지정하면 현재 정의된 네임스페이스에 해당 이름의 클래스가 있다면 클래스 객체를 가져오고 싶습니다.기본적으로 다음과 같은 결과를 얻을 수 있는 기능을 구현하고 싶습니다.

class Foo:
    pass

str_to_class("Foo")
==> <class __main__.Foo at 0x69ba0>

이게, 정말 가능한 일인가요?

이것은 동작할 수 있습니다.

import sys

def str_to_class(classname):
    return getattr(sys.modules[__name__], classname)

경고:eval()임의의 Python 코드를 실행하는 데 사용할 수 있습니다.사용해서는 안 됩니다.eval()신뢰할 수 없는 문자열로.(신뢰할 수 없는 문자열에 대한 Python's eval() 보안 참조)

이게 제일 간단해 보여요.

>>> class Foo(object):
...     pass
... 
>>> eval("Foo")
<class '__main__.Foo'>

다음과 같은 작업을 수행할 수 있습니다.

globals()[class_name]

수업을 원하십니까?Baz모듈 내에 있습니다.foo.barPython 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

Python < 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')

장고가 이걸 어떻게 처리하는지 봤어요

django.sys.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 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)

구식 및 신식 클래스가 모두 정확하게 처리됩니다.

문자열로 만든 클래스를 가져오려면 사전에 저장(또는 적절한 단어, 참조)해야 합니다.따라서 더 높은 수준의 클래스 이름을 지정하고 원치 않는 클래스가 노출되는 것을 방지할 수 있습니다.

예를 들어, Python에서 배우 클래스가 정의되어 사용자 입력으로 다른 일반 클래스가 도달하는 것을 피하고 싶은 게임입니다.

또 다른 접근법(아래 예시와 같이)은 완전히 새로운 클래스를 만듭니다.dict위. 이것은 다음과 같습니다.

  • 복수의 클래스 홀더를 보다 쉽게 편성할 수 있도록 한다(배우 클래스용과 음향 타입용).
  • 보유자와 보유 중인 클래스 모두를 더 쉽게 수정할 수 있도록 한다.
  • 또한 클래스 메서드를 사용하여 dict에 클래스를 추가할 수 있습니다. (아래 추상화는 실제로 필요하지 않지만, 단지...을 위한 것입니다.'정보')를 참조해 주세요.

예:

class ClassHolder:
    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:
    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:
    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그럼 지금까지의 모든 수업에서 절대 빠지지 않을 거예요:^)

업데이트: 데코레이터를 줄임말로 사용할 수도 있습니다.

class ClassHolder:
    def __init__(self):
        self.classes = {}

    def add_class(self, c):
        self.classes[c.__name__] = c

    # -- the decorator
    def held(self, c):
        self.add_class(c)

        # Decorators have to return the function/class passed (or a modified variant thereof), however I'd rather do this separately than retroactively change add_class, so.
        # "held" is more succint, anyway.
        return c 

    def __getitem__(self, n):
        return self.classes[n]

food_types = ClassHolder()

@food_types.held
class bacon:
    taste = "salty"

@food_types.held
class chocolate:
    taste = "sweet"

@food_types.held
class tee:
    taste = "bitter" # coffee, ftw ;)

@food_types.held
class lemon:
    taste = "sour"

print(food_types['bacon'].taste) # No manual add_class needed! :D

네, 할 수 있어요.클래스가 글로벌 이름 공간에 있다고 가정하면 다음과 같은 작업이 수행됩니다.

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>

임의의 코드 실행이나 바람직하지 않은 사용자가 전달한 이름의 관점에서 허용 가능한 함수/클래스 이름 목록을 가질 수 있으며, 입력이 목록의 이름과 일치하면 eval'd가 됩니다.

PS: 알아요...좀 늦었네요...나중에 이걸 우연히 발견하는 사람을 위한 거예요

는 importlib을 사용하는 것이 가장 효과적이었습니다.

import importlib

importlib.import_module('accounting.views') 

Import하는 python 모듈에 문자열 도트 표기가 사용됩니다.

언급URL : https://stackoverflow.com/questions/1176136/convert-string-to-python-class-object

반응형