Convert string to Python class object?

匿名 (未验证) 提交于 2019-12-03 02:14:01

问题:

Given a string as user input to a python function, I'd like to get a class object out of it if there's a class with that name in the currently defined namespace. Essentially, I want the implementation for a function which will produce this kind of result:

class Foo:     pass  str_to_class("Foo") ==> 

Is this possible?

回答1:

This seems simplest.

>>> class Foo(object): ...     pass ...  >>> eval("Foo") 


回答2:

This could work:

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


回答3:

You could do something like:

globals()[class_name] 


回答4:

You want the class "Baz", which lives in module "foo.bar". With python 2.7, you want to use importlib.import_module(), as this will make transitioning to python 3 easier:

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 

With python

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 

Use:

loaded_class = class_for_name('foo.bar', 'Baz') 


回答5:

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) 

This accurately handles both old-style and new-style classes.



回答6:

I've looked at how django handles this

django.utils.module_loading has this

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

You can use it like import_string("module_path.to.all.the.way.to.your_class")



回答7:

Yes, you can do this. Assuming your classes exist in the global namespace, something like this will do it:

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')  ==> 


回答8:

In terms of arbitrary code execution, or undesired user passed names, you could have a list of acceptable function/class names, and if the input matches one in the list, it is eval'd.

PS: I know....kinda late....but it's for anyone else who stumbles across this in the future.



回答9:

Using importlib worked the best for me.

import importlib  importlib.import_module('accounting.views')  

This uses string dot notation for the python module that you want to import.



回答10:

If you really want to retrieve classes you make with a string, you should store (or properly worded, reference) them in a dictionary. After all, that'll also allow to name your classes in a higher level and avoid exposing unwanted classes.

Example, from a game where actor classes are defined in Python and you want to avoid other general classes to be reached by user input.

Another approach (like in the example below) would to make an entire new class, that holds the dict above. This would:

  • Allow multiple class holders to be made for easier organization (like, one for actor classes and another for types of sound);
  • Make modifications to both the holder and the classes being held easier;
  • And you can use class methods to add classes to the dict. (Although the abstraction below isn't really necessary, it is merely for... "illustration").

Example:

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!" 

This returns me:

<__main__.classholder object="" at=""><__main__.classholder object="" at=""> ======= o  o  ======= Hello World! ======= 0 1 -- 2 6 -- Done experiment! 

Another fun experiment to do with those is to add a method that pickles the ClassHolder so you never lose all the classes you did :^)



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!