【问题标题】:Generate a list of class members that preserves their definition order生成保留其定义顺序的类成员列表
【发布时间】:2015-04-10 19:49:04
【问题描述】:

我正在尝试根据一些 Python 类的定义自动创建一些 SQL 表,我尝试使用 dir(),但由于它返回一个 Python 字典,因此它没有排序,因此类成员的定义顺序丢失了。

在网上阅读我发现了以下here

class OrderedClass(type):

     @classmethod
     def __prepare__(metacls, name, bases, **kwds):
        return collections.OrderedDict()

     def __new__(cls, name, bases, namespace, **kwds):
        result = type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

class A(metaclass=OrderedClass):
    def one(self): pass
    def two(self): pass
    def three(self): pass
    def four(self): pass

>>> A.members
('__module__', 'one', 'two', 'three', 'four')

我成功地实现了它的一个副本,它似乎正在做它应该做的事情,只是它只将methods 保存在members 变量中,而且我还需要有 class 成员变量

问题

如何获得保留其定义顺序的成员变量列表?我不关心类方法,实际上我忽略了它们。

注意:之所以顺序很重要,是因为表会有引用某些表列的约束,而且必须在定义列之后,而它们却出现在之前。

编辑:这是我真实程序中的示例类

class SQLTable(type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwds):
        return OrderedDict()

    def __new__(cls, name, bases, namespace, **kwds):
        result = type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

class AreaFisicoAmbiental(metaclass = SQLTable):
    def __init__(self, persona, datos):
        # edificacion
        self.persona = persona
        self.tipoEdificacion = datos[0]
        self.tipoDeParedes = datos[1]
        self.detallesTipoDeParedes = datos[2]
        self.tipoDeTecho = datos[3]
        self.detallesTipoDeTecho = datos[4]
        self.tipoDePiso = datos[5]
        self.detallesTipoDePiso = datos[6]
        # ambientes
        self.problemaDeInfraestructura = datos[7]
        self.detallesProblemaDeInfraestructura = datos[9]
        self.condicionDeTenencia = datos[10]
        self.detallesCondicionDeTenencia = datos[11]
        self.sala = toBool(datos[12])
        self.comedor = toBool(datos[13])
        self.baño = toBool(datos[14])
        self.porche = toBool(datos[15])
        self.patio = toBool(datos[16])
        self.lavandero = toBool(datos[17])
        self.habitaciones = toInt(datos[19])
        # servicios básicos
        self.aguasServidas = toBool(datos[21])
        self.aguaPotable = toBool(datos[22])
        self.luz = toBool(datos[23])
        self.gas = datos[24]
        self.internet = toBool(datos[25])

在做

print(AreaFisicoAmbiental.members)

输出:

('__module__', '__qualname__', '__init__')

变量名称是西班牙语,因为它们的名称将用作表列名称,也用作将从数据库结构生成的 Web 应用程序的标签。

我知道 Django 会做这样的事情,但我已经有我的数据库检查器来做相反的事情,所以知道我需要一个类似 Django 的功能来使用我的生成器。

【问题讨论】:

  • 你有没有想过为此使用 Django?或者至少看看它的模型代码是如何工作的?它正是这样做的。
  • class A 没有定义任何成员变量。尝试在其定义中添加member_var = 42。无论如何,听起来您真正想要的是实例数据成员的副本。
  • @martineau 我的类确实定义了成员变量,我将发布其中一个。
  • 既然这就是您的问题的全部内容,请务必这样做。
  • 所有用self.xxx = whatever 定义的项目都是实例属性,在类定义时不存在。

标签: python python-3.x introspection metaclass


【解决方案1】:

更新

正如我所评论的,我认为您可能将实例属性与类属性混淆了,并且真的想跟踪后者。实例属性是动态的,可以随时添加、更改或删除,因此尝试使用您的问题中显示的元类来执行此操作是行不通的(并且不同的实例可能定义了不同的组)。

您可以通过重载类的几个特殊方法(即__setattr__()__delattr__())来跟踪它们的创建和删除,并将它们的效果存储在一个私有数据成员(即OrderedSet)中。这样做将跟踪它们是什么并保留它们的创建顺序。

这两种方法都需要注意不要对私有数据成员本身进行操作。

也就是说,这里有一些说明这种实现的东西:

# -*- coding: iso-8859-1 -*-
# from http://code.activestate.com/recipes/576694
from orderedset import OrderedSet

class AreaFisicoAmbiental(object):
    def __init__(self, persona, datos):
        self._members = OrderedSet()
        self.persona = persona
        self.tipoEdificacion = datos[0]
        self.tipoDeParedes = datos[1]

    def __setattr__(self, name, value):
        object.__setattr__(self, name, value)
        if name != '_members':
            self._members.add(name)

    def __delattr__(self, name):
        if name != '_members':
            object.__delattr__(self, name)
            self._members.discard(name)

    def methodA(self, value1, value2):  # add some members
        self.attribute1 = value1
        self.attribute2 = value2

    def methodB(self):
        del self.attribute1  # remove a member

if __name__ == '__main__':
    a = AreaFisicoAmbiental('Martineau', ['de albañilería', 'vinilo'])
    a.methodA('attribute1 will be deleted', 'but this one will be retained')
    a.methodB()  # deletes a.attribute1
    a.attribute3 = 42  # add an attribute outside the class

    print('current members of "a":')
    for name in a._members:
        print('  {}'.format(name))

输出:

current members of "a":
  persona
  tipoEdificacion
  tipoDeParedes
  attribute2
  attribute3

最后一点:可以创建一个元类,将这两种方法自动添加到客户端类中,这样可以更轻松地修改现有类。

【讨论】:

    【解决方案2】:

    也许,python enum 足以完成这项任务。确实支持稳定订单。

    DDL 的基本实现如下所示:

    from enum import Enum
    class Table1(Enum):
        nombre = ''
        edad = 0
        sexo = True
        ...
    

    然后你可以这样做:

    for prop in Table1:
        print(prop)
    

    这给了你

    Table1.nombre
    Table1.edad
    Table1.sexo
    

    如果你需要构造一个合适的表定义,你可以使用 Table1..value:

    >>> print(type(Table1.nombre.value))
    <class 'str'>
    >>> print(type(Table1.edad.value))
    <class 'int'>
    

    等等。使用这种技术,您甚至可以将一些表链接到其他表,从而构建一整套表及其关系的完整定义。

    至于数据对象(例如表中的一行,或者查询结果的一行),这里我认为你没有任何自己的排序,你只需要维护一个到相应表类的链接(从中订单可以恢复,但我不认为这是一个要求的选项)。所以这些类可能看起来像这样:

    class Table1Row(object):
        _table = Table1
        __slots__ = tuple(k.name for k Table1)
        ...
    

    或者干脆

    class ASpecificQueryResults(object):
       __slots__ = (Table1.nombre.name, Table2.empresa.name,...)
    

    您可能需要一个工厂来根据查询结果和/或表定义构建行类。

    编辑可能在 *Row 类中使用 __slots__ 的想法需要更多润色,但这在很大程度上取决于您的实际需求。

    附:也许“Table1.sexo”在我们复杂的时代也应该是一个枚举;)

    【讨论】:

    • 这将是一个具有特定数量的实例的类,按顺序,而不是一个实例的属性按特定顺序排列的类。
    • 好吧,据我所知@¡harob 需要这个来实现某种 ORM。在这种情况下,只有一个类的实例很重要(它对应于一个 SQL 表),甚至不是一个实例,而是一个具有一些类级别属性的类。所以我仍然认为像基于枚举的类之类的东西就足以解决他最初的问题
    • @user3159253 也许你的想法还不错,但缺乏解释,你会如何使用python enum来实现,哪怕是一个简单的例子?
    • @user3159253 你做得很好,让我看看它是否符合我的需求,我需要立即完成这个项目。
    • 在 PS:确实如此。我的西班牙语非常生疏,但如果 sexo 表示 sex (而 gender 是不同的词),它也应该重命名。至少花两分钟时间和skim relevant Wikipedia articles。你甚至可能学到一些东西。
    猜你喜欢
    • 1970-01-01
    • 2015-06-06
    • 2023-04-09
    • 2015-05-12
    • 1970-01-01
    • 2021-09-21
    • 2010-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多