파이썬에는 Java Class.forName ()과 동등한 것이 있습니까?


95

문자열 인수를 가져와 Python에서 해당 문자열로 명명 된 클래스의 개체를 만들어야합니다. Java에서는 Class.forName().newInstance(). 파이썬에 동등한 것이 있습니까?


응답 해 주셔서 감사합니다. 내가하는 일을 알고 싶은 사람들에게 대답하려면 : 명령 줄 인수를 클래스 이름으로 사용하고 인스턴스화하고 싶습니다. 나는 실제로 Jython으로 프로그래밍하고 Java 클래스를 인스턴스화하고 있으므로 Java-ness가 질문입니다. getattr()잘 작동합니다. 고맙습니다.


사용 예제는 stackoverflow.com/a/10675081/116891 을 참조하십시오. importlib.importpython 3에서 2.7로 백 포트되었습니다 ( docs.python.org/2/library/importlib.html )
Pat

답변:


169

파이썬에서의 리플렉션은 자바보다 훨씬 쉽고 훨씬 더 유연합니다.

튜토리얼을 읽는 것이 좋습니다.

정규화 된 클래스 이름을 가져 와서 클래스를 반환하는 직접 함수 (내가 알고있는)는 없지만이를 빌드하는 데 필요한 모든 부분이 있고 서로 연결할 수 있습니다.

하지만 한 가지 조언 : 파이썬에서 자바 스타일로 프로그래밍하지 마십시오.

하려는 것이 무엇인지 설명해 주시면 더 비단뱀적인 방법을 찾도록 도와 드릴 수 있습니다.

다음은 원하는 작업을 수행하는 함수입니다.

def get_class( kls ):
    parts = kls.split('.')
    module = ".".join(parts[:-1])
    m = __import__( module )
    for comp in parts[1:]:
        m = getattr(m, comp)            
    return m

이 함수의 반환 값을 마치 클래스 자체 인 것처럼 사용할 수 있습니다.

다음은 사용 예입니다.

>>> D = get_class("datetime.datetime")
>>> D
<type 'datetime.datetime'>
>>> D.now()
datetime.datetime(2009, 1, 17, 2, 15, 58, 883000)
>>> a = D( 2010, 4, 22 )
>>> a
datetime.datetime(2010, 4, 22, 0, 0)
>>> 

어떻게 작동합니까?

__import__클래스를 포함하는 모듈을 가져 오기 위해를 사용 하고 있으며, 먼저 정규화 된 이름에서 모듈 이름을 추출해야합니다. 그런 다음 모듈을 가져옵니다.

m = __import__( module )

이 경우 m최상위 모듈 만 참조합니다.

예를 들어 클래스가 foo.baz모듈에 있으면 m모듈이 foo
됩니다. foo.baz사용에 대한 참조를 쉽게 얻을 수 있습니다.getattr( m, 'baz' )

최상위 모듈에서 클래스 gettatr로 이동하려면 클래스 이름의 일부 를 재귀 적으로 사용해야 합니다.

예를 들어, 클래스 이름이 foo.baz.bar.Model다음 과 같으면 다음을 수행합니다.

m = __import__( "foo.baz.bar" ) #m is package foo
m = getattr( m, "baz" ) #m is package baz
m = getattr( m, "bar" ) #m is module bar
m = getattr( m, "Model" ) #m is class Model

이 루프에서 일어나는 일입니다.

for comp in parts[1:]:
    m = getattr(m, comp)    

루프의 끝 m에서 클래스에 대한 참조가됩니다. 이것은 m실제로 itslef 클래스임을 의미 합니다. 예를 들면 다음과 같습니다.

a = m() #instantiate a new instance of the class    
b = m( arg1, arg2 ) # pass arguments to the constructor

6
Exec은 매우 위험합니다. 스코프의 이름을 동적으로 덮어 쓰고로드 아일랜드 크기의 보안 결함을 여는 것처럼 쉽게 발에 자신을 쏠 수 있습니다.
Cody Brocious

1
여기서 exec가 안전하지 않은 이유는 ';'를 사용할 수 있기 때문입니다. 가져 오기를 중단하려면 클래스 이름에 이것은 임의의 코드 실행을 쉽게 허용 할 수 있습니다. 코드를 손상시킬 수있는 이유는 exec가 충돌하는 이름을 덮어 쓰고 버그 및 / 또는 보안 결함을 유발하기 때문입니다.
Cody Brocious

8
네, 이것이 __import__존재하는 것입니다. 모듈 로딩 마법을 수행하는 Django와 같은 프로젝트는 항상 그것을 사용합니다. 좋은 대답입니다.
cdleary

5
get_class = lambda name: reduce(getattr, name.split('.')[1:], __import__(name.partition('.')[0])). 'object.from_name'이 더 나은 이름 일 수 있지만. 예 : get_class ( 'decimal.Decimal'), get_class (module_name).
jfs

1
7 줄 함수를 정의하기 만하면됩니다. 정말 편안합니다.
Pavel Vlasov 2012

27

클래스가 범위에 있다고 가정합니다.

globals()['classname'](args, to, constructor)

그렇지 않으면:

getattr(someModule, 'classname')(args, to, constructor)

편집 : 참고, getattr에 'foo.bar'와 같은 이름을 지정할 수 없습니다. 으로 분할해야합니다. 왼쪽에서 오른쪽으로 각 부분에 대해 getattr ()을 호출합니다. 이것은 다음을 처리합니다.

module, rest = 'foo.bar.baz'.split('.', 1)
fooBar = reduce(lambda a, b: getattr(a, b), rest.split('.'), globals()[module])
someVar = fooBar(args, to, constructor)

globals () [ 'classname']이어야하지 않습니까?
orip

당신은 정말 맞습니다. globals ()가 실제 전역 범위를 반환하지 않고 매핑하는 것을 잊었습니다. 그렇게 편집-감사합니다!
Cody Brocious

이것은 Java의 Class.forName과 동일하지 않습니다. Java에서는 가져온 항목과 가져 오지 않은 항목에 대한 가정이 없습니다
hasen

1
attrs = 'foo.bar.baz'.split('.'); fooBar = reduce(getattr, attrs[1:], __import__(attrs[0]))
jfs

1
또는module, _, attrs = 'foo.bar.baz'.partition('.'); fooBar = reduce(getattr, attrs, __import__(module))
jfs

15
def import_class_from_string(path):
    from importlib import import_module
    module_path, _, class_name = path.rpartition('.')
    mod = import_module(module_path)
    klass = getattr(mod, class_name)
    return klass

용법

In [59]: raise import_class_from_string('google.appengine.runtime.apiproxy_errors.DeadlineExceededError')()
---------------------------------------------------------------------------
DeadlineExceededError                     Traceback (most recent call last)
<ipython-input-59-b4e59d809b2f> in <module>()
----> 1 raise import_class_from_string('google.appengine.runtime.apiproxy_errors.DeadlineExceededError')()

DeadlineExceededError: 

1
이 필요 (즉, 방지로이 작업을 수행하는 importlib을 사용하기 위해 한 수입 재귀 적으로 클래스를 찾을 원인). 허용되는 답변보다 간단하고 명확합니다 (잘 문서화되지는 않았지만).
sage88

4

또 다른 구현.

def import_class(class_string):
    """Returns class object specified by a string.

    Args:
        class_string: The string representing a class.

    Raises:
        ValueError if module part of the class is not specified.
    """
    module_name, _, class_name = class_string.rpartition('.')
    if module_name == '':
        raise ValueError('Class name must contain module part.')
    return getattr(
        __import__(module_name, globals(), locals(), [class_name], -1),
        class_name)

if module_name당신은 단순히 그 라인을 삭제하면 당신이 얻을 오류이기 때문에 LBYL 꽤 redudant이다 ValueError: Empty module name.
bukzor 2014

3

처음이 아니라 중간에서 접근하는 것 같습니다. 정말로하려는 것이 무엇입니까? 주어진 문자열과 관련된 클래스를 찾는 것은 목적을위한 수단입니다.

자신의 정신적 리팩토링이 필요할 수있는 문제를 명확히하면 더 나은 솔루션이 나타날 수 있습니다.

예 : 유형 이름과 매개 변수 세트를 기반으로 저장된 객체를로드하려고합니까? 파이썬은이 언 피클 링을 철자하며 피클 모듈을 봐야 합니다 . 언 피클 링 프로세스가 설명하는대로 정확히 수행되지만 내부적으로 어떻게 작동하는지 걱정할 필요가 없습니다.

>>> class A(object):
...   def __init__(self, v):
...     self.v = v
...   def __reduce__(self):
...     return (self.__class__, (self.v,))
>>> a = A("example")
>>> import pickle
>>> b = pickle.loads(pickle.dumps(a))
>>> a.v, b.v
('example', 'example')
>>> a is b
False

1
이것은 매우 흥미로운이지만, 질문보다는 약간 다르다

1

이것은 파이썬 표준 라이브러리에서 unittest.TestLoader.loadTestsFromName으로 찾을 수 있습니다. 불행히도이 방법은 추가 테스트 관련 활동을 계속하지만이 첫 번째 ha는 재사용이 가능해 보입니다. 테스트 관련 기능을 제거하기 위해 편집했습니다.

def get_object(name):
    """Retrieve a python object, given its dotted.name."""
    parts = name.split('.')
    parts_copy = parts[:]
    while parts_copy:
        try:
            module = __import__('.'.join(parts_copy))
            break
        except ImportError:
            del parts_copy[-1]
            if not parts_copy: raise
    parts = parts[1:]

    obj = module
    for part in parts:
        parent, obj = obj, getattr(obj, part)

    return obj

0

.NET의 모든 기존 클래스에 대한 개체를 가져와야했습니다 my_package. 그래서 필요한 모든 클래스를 my_package__init__.py.

그래서 내 디렉토리 구조는 다음과 같습니다.

/my_package
    - __init__.py
    - module1.py
    - module2.py
    - module3.py

그리고 내 __init__.py모습은 다음과 같습니다.

from .module1 import ClassA
from .module2 import ClassB

그런 다음 다음과 같은 함수를 만듭니다.

def get_classes_from_module_name(module_name):
    return [_cls() for _, _cls in inspect.getmembers(__import__(module_name), inspect.isclass)]

어디 module_name = 'my_package'

문서 검사 : https://docs.python.org/3/library/inspect.html#inspect.getmembers

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.