Czy python ma odpowiednik klasy Java.forName()?

Muszę wziąć argument string i utworzyć obiekt klasy o nazwie w tym łańcuchu w Pythonie. W Javie użyłbym Class.forName().newInstance(). Czy istnieje odpowiednik w Pythonie?


Dzięki za odpowiedzi. Aby odpowiedzieć tym, którzy chcą wiedzieć, co robię: chcę użyć argumentu wiersza poleceń jako nazwy klasy i utworzyć jej instancję. Właściwie to programuję w Jythonie i tworzymy instancje klas Javy, stąd ta Java-ness tego pytania. Działa świetnie. Wielkie dzięki.
Author: Erik Allik, 2009-01-17

6 answers

Odbicie w Pythonie jest o wiele łatwiejsze i bardziej elastyczne niż w Javie.

Polecam przeczytać ten tutorial

Nie ma bezpośredniej funkcji (o której wiem), która pobiera w pełni kwalifikowaną nazwę klasy i zwraca klasę, jednak masz wszystkie elementy potrzebne do jej zbudowania i możesz je połączyć.

Jedna rada: nie próbuj programować w stylu Java, gdy jesteś w Pythonie.

Jeśli możesz wyjaśnić, co to jest to, co próbujesz zrobić, może pomożemy Ci znaleźć bardziej pythoniczny sposób.

Oto funkcja, która robi to, co chcesz:

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

Możesz użyć wartości zwracanej tej funkcji tak, jakby była to sama klasa.

Oto przykład użycia:
>>> 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)
>>> 

Jak to działa?

Używamy __import__ Aby zaimportować moduł, który posiada klasę, co wymagało, abyśmy najpierw wyodrębnili nazwę modułu z w pełni kwalifikowanej nazwy. Wtedy my import modułu:

m = __import__( module )

W tym przypadku, m będzie odnosić się tylko do modułu najwyższego poziomu,

Na przykład, jeśli twoja klasa mieszka w module foo.baz, to m będzie modułem foo
Możemy łatwo uzyskać odniesienie do foo.baz za pomocą getattr( m, 'baz' )

Aby dostać się z modułu najwyższego poziomu do klasy, należy użyć rekurencyjnie gettatr na częściach nazwy klasy

Powiedzmy na przykład, jeśli nazwa klasy to foo.baz.bar.Model to robimy tak:

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

This is what ' s dzieje się w tej pętli:

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

Na końcu pętli, {[7] } będzie odniesieniem do klasy. Oznacza to, że {[7] } jest w rzeczywistości klasą itslef, można to zrobić na przykład:

a = m() #instantiate a new instance of the class    
b = m( arg1, arg2 ) # pass arguments to the constructor
 148
Author: hasen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-05 21:42:02

Zakładając, że klasa jest w Twoim zakresie:

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

Inaczej:

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

Edit: uwaga, nie możesz podać nazwy takiej jak ' foo.bar ' to getattr. Musisz się podzielić . i wywołaj getattr () na każdym kawałku od lewej do prawej. Poradzi sobie z tym:

module, rest = 'foo.bar.baz'.split('.', 1)
fooBar = reduce(lambda a, b: getattr(a, b), rest.split('.'), globals()[module])
someVar = fooBar(args, to, constructor)
 23
Author: Cody Brocious,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-17 09:24:14
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

Użycie

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: 
 8
Author: Pat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-05 01:00:38

Kolejna implementacja.

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)
 4
Author: grayhemp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-31 14:41:58

Wygląda na to, że podchodzisz do tego od środka, a nie Od początku. Co tak naprawdę próbujesz zrobić? Znalezienie klasy powiązanej z danym łańcuchem jest środkiem do końca.

Jeśli wyjaśnisz swój problem, który może wymagać twojego własnego refaktoryzacji umysłowej, może pojawić się lepsze rozwiązanie.

Na przykład: czy próbujesz załadować zapisany obiekt na podstawie jego nazwy typu i zestawu parametrów? Python pisze to nieporęcznie i powinieneś spojrzeć na ogórek Moduł . I chociaż proces rozpakowywania robi dokładnie to, co opisujesz, nie musisz się martwić o to, jak działa wewnętrznie: {]}

>>> 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
 3
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-17 15:37:40

To znajduje się w standardowej bibliotece Pythona, jako unittest.TestLoader.loadTestsFromName. Niestety metoda ta prowadzi do dodatkowych czynności związanych z testami, ale ten pierwszy ha wygląda na ponownie użyteczny. Edytowałem go, aby usunąć funkcjonalność związaną z testem:

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
 1
Author: bukzor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-10 23:54:55