स्ट्रिंग को पायथन क्लास ऑब्जेक्ट में बदलें?


187

पायथन फ़ंक्शन के उपयोगकर्ता इनपुट के रूप में एक स्ट्रिंग को देखते हुए, मैं उससे एक वर्ग वस्तु प्राप्त करना चाहूंगा यदि वर्तमान में परिभाषित नाम स्थान में उस नाम के साथ एक वर्ग है। अनिवार्य रूप से, मैं एक फ़ंक्शन के लिए कार्यान्वयन चाहता हूं जो इस तरह का परिणाम देगा:

class Foo:
    pass

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

क्या ये संभव भी है?

python 

2
यहाँ कुछ उपयोगी उत्तर हैं, लेकिन मुझे इस प्रश्न का उत्तर विशेष रूप से उपयोगी लगा: stackoverflow.com/questions/452969/…
Tom

जवाबों:


115

चेतावनी : eval()मनमाने पायथन कोड को निष्पादित करने के लिए इस्तेमाल किया जा सकता है। आपको कभी भीeval() अविश्वसनीय तार के साथ उपयोग नहीं करना चाहिए । ( अविश्वसनीय तार पर पायथन के निष्कासन () की सुरक्षा देखें ? )

यह सबसे सरल लगता है।

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

30
Eval का उपयोग करने के असर को खबरदार। आप बेहतर सुनिश्चित करते हैं कि आपके द्वारा पारित स्ट्रिंग उपयोगकर्ता से नहीं है।
ओवरक्लॉक किया

18
Eval () का उपयोग करते हुए मनमानी कोड निष्पादन के लिए दरवाजा खुला छोड़ दिया जाता है, सुरक्षा के लिए इसे बचा जाना चाहिए।
m.kocikowski

54
ईवल कुछ भी करने के लिए दरवाजा खुला नहीं छोड़ता है। यदि आपके पास दुर्भावनापूर्ण, बुरे उपयोगकर्ता हैं, जो दुर्भावनापूर्ण रूप से हो सकते हैं और ईविल खराब करने के लिए बुरे मूल्यों को पारित करते हैं, तो वे बस पायथन स्रोत को संपादित कर सकते हैं। चूंकि वे सिर्फ अजगर स्रोत को संपादित कर सकते हैं, दरवाजा था, था, और हमेशा खुला रहेगा।
एस.लूट।

34
जब तक कि प्रश्न में प्रोग्राम एक सर्वर पर नहीं चल रहा है।
वत्सु 1

12
@ s-lott मुझे क्षमा करें, लेकिन मुझे लगता है कि आप यहां बहुत बुरी सलाह दे रहे हैं। विचार करें: जब आपका कंप्यूटर आपको पासवर्ड के लिए संकेत देता है, तो दुर्भावनापूर्ण, दुर्भावनापूर्ण उपयोगकर्ता बस इसी निष्पादन योग्य या लाइब्रेरी को संशोधित कर सकता है और इस चेक को बायपास कर सकता है। इसलिए, कोई यह तर्क दे सकता है कि पहली जगह में पासवर्ड की जाँच को जोड़ना व्यर्थ है। या यह है?
डैनियल

148

यह काम कर सकता है:

import sys

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

यदि वर्ग मौजूद नहीं है तो क्या होगा?
रिजा जूल

7
यह केवल मौजूदा मॉड्यूल में परिभाषित वर्ग के लिए काम करेगा
आकर्षक

1
वास्तव में अच्छा समाधान: यहां आप आयात का उपयोग नहीं कर रहे हैं, लेकिन एक अधिक सामान्य sys मॉड्यूल।
स्टेफानो फल्सेटो

क्या इससे कोई अलग होगा def str_to_class(str): return vars()[str]?
अर्ने

113

आप कुछ ऐसा कर सकते हैं:

globals()[class_name]

6
मुझे यह 'eval' समाधान से बेहतर लगता है क्योंकि (1) यह उतना ही आसान है, और (2) यह आपको मनमाने ढंग से कोड निष्पादन के लिए खुला नहीं छोड़ता है।
मजुम्बेउ

2
लेकिन आप उपयोग कर रहे हैं globals()... एक और चीज जिसे टाला जाना चाहिए
ग्रेग

5
@Greg शायद, लेकिन globals()यकीनन अभी तक की तुलना में बहुत कम बुरा है eval, और वास्तव में sys.modules[__name__]AFAIK से भी बदतर नहीं है ।
लॉरेंस गोंसाल्वेस

2
हाँ, मैं आपकी बात देख रहा हूँ, क्योंकि आप केवल इससे कुछ भी नहीं निकाल रहे हैं
ग्रेग

यदि किसी वर्ग में चर का उपयोग किया जाता है और उस वर्ग के लिए कई उदाहरण एक ही समय में एक साथ किए गए थे। क्या यह समस्या पैदा करेगा, क्योंकि यह एक वैश्विक चर है।
आलोक कुमार सिंह

98

आप वर्ग चाहते हैं Baz, जो मॉड्यूल में रहता है foo.bar। पायथन 2.7 के साथ, आप इसका उपयोग करना चाहते हैं importlib.import_module(), क्योंकि इससे पायथन 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')

19
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)

यह पुरानी शैली और नई शैली की कक्षाओं दोनों को सटीक रूप से संभालता है।


आपको टाइप्स की आवश्यकता नहीं है। टाइप टाइप; यह बिलिन "प्रकार" के लिए सिर्फ एक उपनाम है।
ग्लेन मेनार्ड

1
हाँ, मुझे पता है, लेकिन मुझे लगता है कि यह सिर्फ पढ़ने के लिए स्पष्ट है। मुझे लगता है कि यह सिर्फ वरीयता के लिए उबलता है, हालांकि।
इवान फॉसमार्क

17

मैंने देखा है कि कैसे 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")



3

हां, आप यह कर सकते हैं। मान लें कि आपकी कक्षाएं वैश्विक नामस्थान में मौजूद हैं, तो कुछ ऐसा होगा:

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>

1
पायथन 3 में, कोई अधिक क्लास टाइप नहीं है।
रिज जूला

1
आइंस्टीनेंस (x, टाइप) का प्रयोग करें।
ग्लेन मेनार्ड

3

मनमाना कोड निष्पादन, या अवांछित उपयोगकर्ता द्वारा पारित नामों के संदर्भ में, आपके पास स्वीकार्य फ़ंक्शन / वर्ग नामों की एक सूची हो सकती है, और यदि इनपुट सूची में एक से मेल खाता है, तो यह स्पष्ट है।

पुनश्च: मुझे पता है .... थोड़े देर से .... लेकिन यह किसी और के लिए भी है जो भविष्य में इसके पार जाता है।


9
यदि आप सूची को बनाए रखने जा रहे हैं, तो इसके बजाय नाम से वर्ग तक एक तानाशाह बनाए रख सकते हैं। तब eval () की कोई आवश्यकता नहीं है।
फासक्स

3

Importlib का उपयोग करना मेरे लिए सबसे अच्छा काम करता है।

import importlib

importlib.import_module('accounting.views') 

यह उस अजगर मॉड्यूल के लिए स्ट्रिंग डॉट नोटेशन का उपयोग करता है जिसे आप आयात करना चाहते हैं।


3

यदि आप वास्तव में उन कक्षाओं को पुनः प्राप्त करना चाहते हैं जिन्हें आप एक स्ट्रिंग के साथ बनाते हैं, तो आपको उन्हें एक शब्दकोश में संग्रहित (या ठीक से शब्द, संदर्भ ) करना चाहिए । आखिरकार, यह आपकी कक्षाओं को उच्च स्तर पर नाम देने और अवांछित कक्षाओं को उजागर करने से बचने की अनुमति देगा।

उदाहरण, एक गेम से जहां अभिनेता कक्षाएं पायथन में परिभाषित की जाती हैं और आप उपयोगकर्ता इनपुट द्वारा पहुंचने के लिए अन्य सामान्य कक्षाओं से बचना चाहते हैं।

एक और दृष्टिकोण (जैसे नीचे दिए गए उदाहरण में) एक पूरी नई कक्षा बनाना चाहता है, जो 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ताकि आप अपने द्वारा किए गए सभी वर्गों को कभी न खोएं: ^)

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