提取指定的变量名称

时间:2011-09-21 05:46:36

标签: python python-3.x inspect

请参阅下面的更新

我甚至不知道如何为我的问题做一个简短的标题。

在课堂上,我有一些StringField类的类属性:

class Authors(Table):
    # id field is already present 
    first_name = StringField(maxLength=100)
    last_name = StringField(maxLength=100)

StringField构造函数可能会收到一个名为name的参数。如果没有给出,我希望它等于类属性的名称(上例中的first_namelast_name)。

是否可以提取创建的实例将要分配给的变量的名称? 我想我必须使用inspect模块?

我看到Django这样做了:

  

每个字段类型,但ForeignKey,ManyToManyField和   OneToOneField采用可选的第一个位置参数 - a   详细的名字。如果没有给出详细的名称,Django会   使用字段的属性名称自动创建它,进行转换   强调空间。

     

在此示例中,详细名称为“人名”:

     

first_name = models.CharField("person's first name", max_length=30)

     

在此示例中,详细名称为“名字”:

     

first_name = models.CharField(max_length=30)

但我没有在Django 1.3.1源代码中找到正在做我需要的部分。

更新

简化:

class Field():
    def __init__(self, field_name=None):
        if not field_name:
            field_name = ??? # some magic here to determine the name
        print(field_name)

class Table():
    first_name = Field()
    last_name = Field()

运行此功能应打印first_namelast_name

class Field():
    def __init__(self, name=None):
        self._name = name

class Table():
    first_name = Field()
    last_name = Field()



for attrName, attr in Table.__dict__.items():
    if isinstance(attr, Field):
        if attr._name is None:
            attr._name = attrName

print(Table.first_name._name)
print(Table.last_name._name)

2 个答案:

答案 0 :(得分:2)

我不知道Django是如何做到的。但你可以这样做:

class WantFixup(object):
    def new_instance(self, name, derived_name):
        cls = type(self)
        if name is None:
            name = derived_name.replace('_', ' ')
        return cls(name)

class Container(WantFixup):
    def __init__(self, name=None):
        self.name = name
    def __repr__(self):
        return "Container('%s')" % str(self.name)

class WillFixup(object):
    def __init__(self):
        cls = type(self)
        for name in cls.__dict__:
            o = cls.__dict__[name] # look up object from name
            if not isinstance(o, WantFixup):
                continue
            print("calling %s.new_instance('%s', '%s')" % (o, o.name, name))
            self.__dict__[name] = o.new_instance(o.name, name)

class Name(WillFixup):
    first_name = Container("given name")
    last_name = Container()

以下是上述代码的示例:

>>> import auto_name
>>> n = auto_name.Name()
calling Container('None').new_instance('None', 'last_name')
calling Container('given name').new_instance('given name', 'first_name')
>>> print(n.__dict__)
{'first_name': Container('given name'), 'last_name': Container('last name')}
>>> print(auto_name.Name.__dict__)
{'__module__': 'auto_name', 'last_name': Container('None'), 'first_name': Container('given name'), '__doc__': None}
>>> 

班级WantFixup有两个目的。首先,可以使用isinstance()检测从中继承的所有类;如果我们的对象实例名为o,我们可以像isinstance(o, WantFixup)一样测试它。其次,它为任何继承它的类提供了.new_instance()方法函数。

Container是可能需要修复的容器的示例。请注意,它继承自WantFixup

WillFixup包含一个.__init__()方法,该方法对从中继承的所有类执行修复。这只是循环遍历类字典中的所有内容,并为每个字符调用.new_instance()方法函数,并传入名称。

最后,班级Name继承自WillFixup,并包含两个Container个实例。因为它继承自WillFixup,所以调用方法WillFixup.__init__()。正如您从示例中看到的那样,first_name的{​​{1}}属性设置为.name'given name'未设置,因此修补后将last_name修改为.name }属性设置为'last name'

.__init__()函数应该设置新的类实例。只要所有特殊WantFixup类实例都在父类中,.__init__()方法就会自动循环遍历它们并进行设置。

这里令人困惑的部分是实例将first_name设置为Container的实例,其名称已修补,并且实际上将用于存储内容。但是,类Name包含Container的实例,该实例仅用于存储类的名称,并作为.__init__()方法的标记来查找。

好的部分是魔法隐藏在基类中。 ContainerName类只需要从它们继承,但它们本身并不是很混乱。

使用元编程可能有一种更灵活的方法来解决问题。

http://www.ibm.com/developerworks/linux/library/l-pymeta/index.html

这个解决方案不是元类编程,而是经过测试,工作代码。

编辑:这是代码的更改版本。原始代码旨在显示一般概念,但实际上并未初始化Name对象。实际执行init并不难,所以我改了它。

答案 1 :(得分:1)

为了让魔术在样本中发生,Python需要是一种上下文敏感的语言(据我所知,这不是那么远)。 Django使用ModelBase元类(以及其他任务)为字段设置详细名称。基本上,元类的__new__遍历类属性以获取属性名称,并将它们添加到options。你可以更直接一点,直接改变字段。这是一个Python 2示例:

class Field(object):
    def __init__(self, name=None):
        self.name = name
    def __str__(self):
        if self.name:
            return self.name
        return type(self).__name__
    def __repr__(self):
        return '%s(%s)' % (type(self).__name__, repr(self.name))

class MetaContainer(type):
    @classmethod
    def dub(metacls, name):
        return name.replace('_', ' ').capitalize()

    def __new__(cls, name, bases, attrs):
        for attr in attrs:
            if issubclass(type(attrs[attr]), Field) and attrs[attr].name is None:
                attrs[attr].name = MetaContainer.dub(attr)
        return super(MetaContainer, cls).__new__(cls, name, bases, attrs)

class Container(object):
    __metaclass__ = MetaContainer
    first_name = Field()
    foo = Field('Foobar')

cntr = Container()
cntr.first_name

Python 3几乎相同,但你使用metaclass class argument * 而不是__metaclass__ property

class Container(object, metaclass=MetaContainer):
    first_name = Field()
    foo = Field('Foobar')

通过直接使用元类创建容器的中间基类,而不是metaclass参数或__metaclass__属性,可以编写与metaclasses in in Python 2 and 3一起使用的版本:

ContainerBase = MetaContainer('ContainerBase', (object,), {})

class Container(ContainerBase):
    first_name = Field()
    foo = Field('Foobar')

* 有关更改的原因,请参阅PEP 3115: Metaclasses in Python 3000