স্ট্রিংটি পাইথন বর্গ অবজেক্টে রূপান্তর করবেন?


187

পাইথন ফাংশনে ব্যবহারকারীর ইনপুট হিসাবে একটি স্ট্রিং দেওয়া হয়েছে, আমি বর্তমানে সংজ্ঞায়িত নেমস্পেসে সেই নামের সাথে কোনও শ্রেণি আছে কিনা তা থেকে একটি শ্রেণির অবজেক্ট পেতে চাই। মূলত, আমি কোনও ফাংশনের জন্য বাস্তবায়ন চাই যা এই ধরণের ফলাফল এনে দেবে:

class Foo:
    pass

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

এটি কি আদৌ সম্ভব?

python 

2
এখানে কিছু দরকারী উত্তর রয়েছে, তবে আমি এই প্রশ্নের উত্তরটি বিশেষভাবে দরকারী বলে খুঁজে পেয়েছি: stackoverflow.com/questions/452969/…
টম

উত্তর:


115

সতর্কতা : eval()স্বেচ্ছাসেবক পাইথন কোডটি কার্যকর করতে ব্যবহার করা যেতে পারে। আপনার কখনইeval() অবিশ্বস্ত স্ট্রিং ব্যবহার করা উচিত নয় । ( অবিশ্বস্ত স্ট্রিংগুলিতে পাইথনের ইওল () এর সুরক্ষা দেখুন ? )

এটি সহজ বলে মনে হচ্ছে।

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

30
ইওল ব্যবহারের ছড়িয়ে পড়া সাবধান। আপনি আরও নিশ্চিত করে নিন যে আপনি যে স্ট্রিংটি পাস করেছেন তা ব্যবহারকারীর কাছ থেকে নেই।
Overclocked

18
ইওল () ব্যবহার করে স্বেচ্ছাসেবী কোড কার্যকর করার জন্য দরজা খোলা থাকে, সুরক্ষার প্রয়োজনে এটি এড়ানো উচিত।
এম.কোসিকোভস্কি

54
ইভাল কোনও কিছুর জন্য দরজা খোলা ছেড়ে দেয় না। আপনার যদি দূষিত, দুষ্ট ব্যবহারকারীরা দূষিতভাবে এবং খারাপভাবে খারাপ মানগুলি প্রকাশের জন্য পাস করতে পারে তবে তারা কেবল অজগর উত্সটি সম্পাদনা করতে পারে। যেহেতু তারা কেবল অজগর উত্সটি সম্পাদনা করতে পারে তাই দরজাটি ছিল, ছিল এবং সর্বদা উন্মুক্ত থাকবে।
এস .লট

34
যতক্ষণ না প্রশ্নে থাকা প্রোগ্রামটি কোনও সার্ভারে চলছে না।
ভ্যাটসু 1

12
@ এস-লট আমি দুঃখিত, তবে আমি মনে করি আপনি এখানে খুব খারাপ পরামর্শ দিচ্ছেন। বিবেচনা করুন: আপনার কম্পিউটার যখন আপনাকে একটি পাসওয়ার্ডের জন্য অনুরোধ জানায়, তখন দুষ্ট, দূষিত ব্যবহারকারীর ঠিক একই সাথে সম্পাদনযোগ্য বা লাইব্রেরি সংশোধন করতে পারে এবং এই চেকটিকে বাইপাস করতে পারে। অতএব, কেউ যুক্তি দিতে পারে যে প্রথমে পাসওয়ার্ড চেক যুক্ত করা অর্থহীন। অথবা এটা?
ড্যানিয়েল এসকি

148

এটি কাজ করতে পারে:

import sys

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

শ্রেণীর অস্তিত্ব না থাকলে কী হবে?
রিজা

7
এটা শুধুমাত্র বর্গ বর্তমান মডিউল সংজ্ঞায়িত জন্য কাজ করবে
Luc

1
সত্যিই দুর্দান্ত সমাধান: এখানে আপনি আমদানি ব্যবহার করছেন না বরং আরও জেনেরিক সিস্ট মডিউল ব্যবহার করছেন।
স্টেফানো ফ্যালসেটো

এটি থেকে অন্য কিছু হতে পারে def str_to_class(str): return vars()[str]?
আর্ন

113

আপনি যেমন কিছু করতে পারেন:

globals()[class_name]

6
আমি এটি 'ইওল' সমাধানের চেয়ে আরও ভাল পছন্দ করি কারণ (1) এটি ঠিক তত সহজ এবং (২) এটি আপনাকে স্বেচ্ছাচারিত কোড প্রয়োগের জন্য উন্মুক্ত রাখে না।
mjumbewu

2
তবে আপনি ব্যবহার করছেন globals()... আর একটি বিষয় যা এড়ানো উচিত
গ্রেগ

5
@ গ্রেগ সম্ভবত, তবে globals()এটি এএফএইকের চেয়ে খারাপ কিছু নয় eval, বরং সত্যই খারাপ sys.modules[__name__]
লরেন্স গনসালভেস

2
হ্যাঁ আমি আপনার বক্তব্যটি দেখতে পাচ্ছি, কারণ আপনি কেবল এটি থেকে কিছুই ধরছেন না
গ্রেগ

চলকটি যদি কোনও শ্রেণিতে এবং একাধিক উদাহরণের জন্য ব্যবহৃত হয় তবে সেই ক্লাসটি একই সাথে একই সময়ে তৈরি হয়েছিল। এটি বৈশ্বিক পরিবর্তনশীল হিসাবে এটি সমস্যার কারণ হবে।
অলোক কুমার সিংহ

98

আপনি ক্লাস চান Baz, যা মডিউল বাস foo.bar। পাইথন ২.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)

এটি পুরানো-শৈলী এবং নতুন-শৈলী উভয় শ্রেণীর সঠিকভাবে পরিচালনা করে।


আপনার টাইপগুলির দরকার নেই ype টাইপ টাইপ; এটি বিল্টিন "টাইপ" এর জন্য কেবল একটি উপনাম।
গ্লেন মেইনার্ড

1
হ্যাঁ আমি জানি, তবে আমি মনে করি এটি পড়ার জন্য আরও পরিষ্কার। আমি অনুমান করি এটি স্রেফ পছন্দের দিকে ফোটে।
ইভান ফসমার্ক

17

আমি দেখেছি কীভাবে জাঙ্গো এটি পরিচালনা করে

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
ইসিনস্ট্যান্স (এক্স, টাইপ) ব্যবহার করুন।
গ্লেন মেইনার্ড

3

স্বেচ্ছাসেবামূলক কোড কার্যকরকরণ, বা অনাকাঙ্ক্ষিত ব্যবহারকারীর পাস করা নামগুলির ক্ষেত্রে, আপনার কাছে গ্রহণযোগ্য ফাংশন / শ্রেণীর নামের একটি তালিকা থাকতে পারে এবং যদি ইনপুট তালিকার কোনওটির সাথে মিলে যায়, তবে তা বিস্মৃত হবে।

পিএস: আমি জানি .... দেরি করে দেরী .... তবে এটি ভবিষ্যতের যে কেউ হোঁচট খায় তার পক্ষে এটি।


9
আপনি যদি তালিকাটি বজায় রাখতে চলেছেন তবে পরিবর্তে নাম থেকে ক্লাসে ডিকটি বজায় রাখতে পারেন। তাহলে এভাল () এর দরকার নেই।
ফ্যাসাক্সেক

3

আমদানি ব্যবহার করা আমার পক্ষে সবচেয়ে ভাল কাজ করেছে।

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যাতে আপনি আপনার সমস্ত ক্লাসটি কখনই হারাতে পারেন না pick

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.