【问题标题】:How do you pass arguments to __metaclass__ in Python如何在 Python 中将参数传递给 __metaclass__
【发布时间】:2018-05-28 16:13:54
【问题描述】:

我有一个用于行数据的元类。任何记录都应该是这个类所固有的,所以你可以在下面看到我试图固有两次,一次用于程序,一次用于资产。但是我需要将 OrderedDict 传递给元类,或者将 slot 和 init 函数移动到实际的类中……但这似乎是在浪费空间。

###############################################################
#NORMALIZED CLASS ROWS 
###############################################################
class MetaNormRow(type, OrderedDict):
    __slots__ = list(OrderedDict.keys())

    def __init__(self, **kwargs):
        for arg, default in OrderedDict.items():
            setattr(self, arg, re.sub(r'[^\x00-\x7F]', '', kwargs.get(arg, default)))
            print (str(arg) + " : "+ str(re.sub(r'[^\x00-\x7F]', '', kwargs.get(arg, default))))

    def items(self):
        for slot in self.__slots__:
            yield slot, getattr(self, slot)

    def values(self):
        for slot in self.__slots__:
            yield getattr(self, slot)

class NormAsset(object):
    __metaclass__ = MetaNormRow(DefaultAsset)

class NormProg(object):
    __metaclass__ = MetaNormRow(DefaultProgs)

下面是我将如何使用 NormAsset 和 Prog 类:

kwargs = {
    "status": norm_status,
    "computer_name": norm_comp_name,
    "domain_name": norm_domain,
    "serial_num": norm_serial,
    "device_type": norm_device_type,
    "mfr": norm_mfr,
    "model": norm_model,
    "os_type": norm_os_type,
    "os_ver": norm_os_ver,
    "os_subver": norm_os_subver,
    "location_code": norm_location_code,
    "tan_id": tan_id,
    "tan_comp_name": tan_comp_name,
    "tan_os": tan_os,
    "tan_os_build": tan_os_build,
    "tan_os_sp": tan_os_sp,
    "tan_country_code": tan_country_code,
    "tan_mfr": tan_mfr,
    "tan_model": tan_model,
    "tan_serial": tan_serial
}
norm_tan_dict[norm_comp_name] = rows.NormAsset(**kwargs)

为了澄清,以下功能 100% 有效...但我需要其中的 10 个,唯一不同的是 DefaultAsset 字典...所以我觉得应该有一种方法可以做到这一点而无需重复每个类...类继承的全部要点:

class NormAsset(object):
    __slots__ = list(DefaultAsset.keys())

    def __init__(self, **kwargs):
        for arg, default in DefaultAsset.items():
            setattr(self, arg, re.sub(r'[^\x00-\x7F]', '', kwargs.get(arg, default)))
            #print (str(arg) + " : "+ str(re.sub(r'[^\x00-\x7F]', '', kwargs.get(arg, default))))

    def items(self):
        for slot in self.__slots__:
            yield slot, getattr(self, slot)

    def values(self):
        for slot in self.__slots__:
            yield getattr(self, slot)

【问题讨论】:

  • You should switch to version 3.6..。我想我从来没有在元类中看到过 dunder init,但我当然没有经验。您在哪里遇到过以这种方式构建事物的方式?
  • 不确定...我已经很多年没有做过这种深度继承了,而且是在 C++ 中。底线......一个 Row 对象,如一个记录类对象,应该是标准的......这意味着我不应该仅仅因为行数据发生变化而在行的每次迭代中重复此 Row 概念共有的任何代码。跨度>
  • afaik 元类实际上并不是关于继承或子类化。元类可用于控制创建一个类。您应该搜索 python 元类的视频并观看一些 - pyvideo.org 上的 Pycon 视频通常都不错。
  • 另外,对“非 ASCII 文本”的仇恨是什么?其他字符是确定性的,只需使用显式编码,您就可以让您的程序处理来自现实世界的数据(使用“ressumé”等外来词)。我建议阅读以下文章。请不要被它的标题冒犯或害怕joelonsoftware.com/2003/10/08/…
  • 这看起来不像是元类的用例...

标签: python inheritance arguments metaclass


【解决方案1】:

你需要的只是普通的类继承,也许是一个普通的函数来作为你的类的工厂。

由于您唯一需要的是键的有序列表,它出现在类的插槽中,就是这样 - 您可以使用已有的代码构建一个基类,并简单地从它继承你的类结构。

如果您想要映射的完整功能,例如 dict,能够检索元素、获取长度等,我建议从 collections.abc.MutableMapping 继承而不是 OrderedDict。即使是因为,如果您从 OrderedDict 继承,__slots__ 声明将毫无价值 - dicts 和 OrderedDict 以 Python 代码无法访问的方式排列它们的数据 - 您可以将代码仅保留在每个类中 __slots__

此外,collections.abc.MutableMapping 的制作方式要求您实现最少的方法集,从中它从 dict 派生所有功能。

所以,调整你的上一个示例类,你会有这样的东西。

from collections.abc import MutableMapping

class BaseAsset(MutableMapping):
    # Base class for slotted classes need to have __slots__.
    __slots__ = []
    default_asset = None

    def __init__(self, **kwargs):
        for arg, default in self.__class__.default_asset.items():
            value = kwargs.get(arg, default)
            setattr(self, arg, re.sub(r'[^\x00-\x7F]', '', value))

    def __getitem__(self, item):
        return getattr(self, item)

    def __setitem__(self, item, value):
        if item in self.__slots__:
            return setattr(self, item, value)
        raise KeyError

    def __delitem__(self, item):
        if item in self.__slots__:
            return delattr(self, item)
        raise KeyError

    def __iter__(self):
        yield from iter(self.__slots__)

    def __len__(self):
        return len(self.__slots__)

    def __repr__(self):
        return f"{self.__class__.__name__}(**{dict(self)})"



def asset_class_factory(name, DefaultAsset):

    class CustomAsset(BaseAsset):
        __slots__ = list(DefaultAsset.keys())
        default_asset = DefaultAsset

    CustomAsset.__name__ = name
    return CustomAsset

这是它的工作原理:

In [182]: d = {"banana": "nanica"} 

In [183]: FruitClass = asset_class_factory("FruitClass", d)

In [184]: f = FruitClass()

In [185]: f.banana
Out[185]: 'nanica'

In [186]: f
Out[186]: FruitClass(**{'banana': 'nanica'}

【讨论】:

    猜你喜欢
    • 2017-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-11
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    相关资源
    最近更新 更多