确保两个Python类具有相同的属性

2024-06-29 01:11:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我希望用Python构建类接口,但发现Python缺少接口构造。你知道吗

我需要的是,如果程序员试图在一个类中添加新属性,而没有在另一个类中添加具有相同名称的属性,则会引发一个异常(在编译时或运行时)

举个例子: 你知道吗

class MongoCompany:
    company_name = MongoField()

class ESCompany:
    company_name = ESField()

如果程序员试图向MongoCompany添加字段而不更改ESCompany,则会引发异常。你知道吗

class MongoCompany:
    company_name = MongoField()
    company_phone = MongoField()

class ESCompany:
    company_name = ESField()

MongoCompany.init()

编辑:

背景 这是为了防止程序员修改MongoDB用Mongoengine的Document类声明的模式,而不添加对Elasticsearch dsl的DocType类在另一个文件中声明的Elasticsearch模式的相应修改。你知道吗


Tags: name名称声明属性模式phoneelasticsearchcompany
1条回答
网友
1楼 · 发布于 2024-06-29 01:11:42

耶!一个元类的实际应用程序,它不是为了使用元类而设计的!我们可以编写一个元类,如果类定义中出现意外属性,它将抛出。我们所要做的就是确保你的程序员真正使用它。你知道吗

class RequiredFieldsMeta(type):
    _interface = {'company_name', 'num_employees'}

    def __new__(cls, clsname, bases, attrs):
        for field in RequiredFieldsMeta._interface:
            if field not in attrs:
                raise AttributeError(
                   'Class %s missing required property %s'
                    % (clsname, field))
        for name in attrs:
            if not isdunder(name) and name not in RequiredFieldsMeta._interface:
                raise AttributeError(
                    'Class %s has extra property %s'
                    % (clsname, name))
        return super(RequiredFieldsMeta, cls).__new__(cls, clsname, bases, attrs)

# Works fine:
class MongoCompany(metaclass=RequiredFieldsMeta):
    company_name = 'Mongo Inc.'
    num_employees = 100

# Throws AttributeError:
class ESyCompany(metaclass=RequiredFieldsMeta):
    extra_prop = 'foobar'

Here's快速演示

请注意,我们甚至没有进行实例化:我们的检查在定义类本身时运行。你知道吗

编辑:在我的编辑中,我引用了一个函数is_dunder。这可以像name.startswith('__')或regex或任何你想要的一样简单,只要它去掉python而不是程序员在类上设置的属性。你知道吗


编辑2:只是为了好玩,这里有两个更“优雅”(虽然不太具体)的支票实现:

def __new__(cls, clsname, bases, attrs):
    attr_names = {a for a in attrs if not is_dunder(a)}

    if attr_names.difference(RequiredFieldsMeta._interface):
        raise AttributeError('Class %s has extra properties' % clsname)
    if RequiredFieldsMeta._interface.difference(attr_names):
        raise AttributeError('Class %s missing required properties' % clsname)

    return super(RequiredFieldsMeta, cls).__new__(cls, clsname, bases, attrs)

或者简单地说:

def __new__(cls, clsname, bases, attrs):
    attr_names = {a for a in attrs if not is_dunder(a)}
    if attr_names != RequiredFieldsMeta._interface:
        raise AttributeError(
            'Class %s does not match the required interface' % clsname)
    return super(RequiredFieldsMeta, cls).__new__(cls, clsname, bases, attrs)

相关问题 更多 >