क्या अजगर में Java Class.forName () के बराबर है?


95

मुझे एक स्ट्रिंग तर्क लेने और पायथन में उस स्ट्रिंग में नामित कक्षा का एक ऑब्जेक्ट बनाने की आवश्यकता है। जावा में, मैं उपयोग करूंगा Class.forName().newInstance()। क्या पायथन में एक समान है?


प्रतिक्रियाओं के लिए धन्यवाद। उन लोगों को जवाब देने के लिए जो जानना चाहते हैं कि मैं क्या कर रहा हूं: मैं क्लास नाम के रूप में कमांड लाइन तर्क का उपयोग करना चाहता हूं, और इसे तत्काल करना चाहता हूं। मैं वास्तव में जेथॉन में प्रोग्रामिंग कर रहा हूं और जावा कक्षाओं को तात्कालिक कर रहा हूं, इसलिए प्रश्न का जावा-नेस। getattr()बहुत अच्छा काम करता है। बहुत धन्यवाद।


उपयोग करने के एक उदाहरण के लिए stackoverflow.com/a/10675081/116891 देखें importlib.import, जिसे अजगर 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वास्तव में यह वर्ग है, आप उदाहरण के लिए कर सकते हैं:

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

6
एक्जाम बेहद खतरनाक है। गतिशील रूप से अपने दायरे में नामों को ओवरराइट करके खुद को शूट करना आसान है और रोड आइलैंड के सुरक्षा दोष को खोलना आसान है।
कोडी ब्रोचेस

1
कारण निष्पादन असुरक्षित है यहां आप ';' का उपयोग कर सकते हैं। आयात से बाहर तोड़ने के लिए वर्ग नाम में। यह आसानी से मनमाना कोड निष्पादन की अनुमति दे सकता है। आपके कोड को नुकसान पहुंचाने का कारण यह है कि निष्पादन टकराव के नामों को अधिलेखित कर देगा, जिससे बग और / या सुरक्षा खामियां हो सकती हैं।
कोड़ी ब्रोचेस

8
हां, यह वही है जो __import__मौजूद है। Django जैसी परियोजनाएं जो मॉड्यूल-लोडिंग-मैजिक करती हैं, हर समय इसका उपयोग करती हैं। अच्छा उत्तर।
cdleary

5
get_class = lambda name: reduce(getattr, name.split('.')[1:], __import__(name.partition('.')[0]))। हालांकि 'object.from_name' एक बेहतर नाम हो सकता है। उदाहरण: get_class ('दशमलव .ecimal'), get_class (मॉड्यूल_name)।
jfs

1
यह केवल सात लाइन फ़ंक्शन को परिभाषित करने के लिए लिया गया था। एक वास्तविक सहजता।
पावेल वलसोव

27

यह मानते हुए कि वर्ग आपके दायरे में है:

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

अन्यथा:

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

संपादित करें: ध्यान दें, आप getattr को 'foo.bar' जैसा नाम नहीं दे सकते। आपको इसे विभाजित करके देखना होगा। और प्रत्येक टुकड़े को बाएं-दाएं पर गेटअटर () कहें। यह संभाल लेगा:

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

क्या यह ग्लोबल्स () ['क्लासनाम'] नहीं होना चाहिए?
orip

आप वास्तव में सही हैं। मैं ग्लोबल्स भूल गया () वास्तविक वैश्विक गुंजाइश नहीं लौटाता, बस इसका एक मैपिंग है। जैसे संपादन - धन्यवाद!
कोडी ब्रोशर

यह जावा के Class.forName के बराबर नहीं है, जावा में, इस बारे में कोई धारणा नहीं बनाई गई है कि क्या आयात किया गया है और क्या नहीं
hasen

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

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
+1 आयात करने के लिए इसे करने के लिए क्योंकि यह आवश्यकता को रोकता है (जो आयात का कारण बनता है) पुनरावर्ती वर्ग को खोजने के लिए। स्वीकृत उत्तर की तुलना में सरल और क्लीनर (हालांकि कम अच्छी तरह से प्रलेखित)।
ऋषि 88

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_nameLBYL, सुंदर redudant है के बाद से त्रुटि अगर आप बस उन पंक्तियों को नष्ट आप प्राप्त है: ValueError: Empty module name
bukzor

3

ऐसा लगता है कि आप शुरुआत के बजाय इसे बीच से हटा रहे हैं। आप वास्तव में क्या करने की कोशिश कर रहे हैं? किसी दिए गए स्ट्रिंग से जुड़े वर्ग को ढूंढना एक अंत का साधन है।

यदि आप अपनी समस्या को स्पष्ट करते हैं, जिसे आपके स्वयं के मानसिक रूप से सुधार की आवश्यकता हो सकती है, तो एक बेहतर समाधान स्वयं उपस्थित हो सकता है।

उदाहरण के लिए: क्या आप इसके प्रकार के नाम और मापदंडों के एक सेट के आधार पर किसी सहेजे गए ऑब्जेक्ट को लोड करने की कोशिश कर रहे हैं? पायथन इस अनप्लिकिंग को मंत्र देता है और आपको अचार मॉड्यूल को देखना चाहिए । और भले ही unpickling प्रक्रिया वही करती है जो आप वर्णन करते हैं, आपको चिंता करने की ज़रूरत नहीं है कि यह आंतरिक रूप से कैसे काम करता है:

>>> 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। दुर्भाग्य से विधि अतिरिक्त परीक्षण-संबंधित गतिविधियों को करने के लिए जाती है, लेकिन यह पहला हा फिर से प्रयोग करने योग्य लगता है। मैंने इसे परीक्षण-संबंधी कार्यक्षमता को हटाने के लिए संपादित किया है:

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

मुझे सभी मौजूदा वर्गों के लिए ऑब्जेक्ट प्राप्त करने की आवश्यकता थी 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'

doc का निरीक्षण करें : https://docs.python.org/3/library/inspect.html#inspect.getmembers

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.