2

I have a metaclass for Row data. Any records at all should inherent from this class, so you can see below I'm trying to inherent twice, once for programs and once for assets. But I need to pass an OrderedDict to the metaclass or alteranative move the slot and init functions to the actual classes... but that seems like a waste of space.

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

Here is how I will use the NormAsset and Prog classes:

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)

To clarify, the following functions works 100%... but I need like 10 of these, the only thing that differs is the DefaultAsset diction... so I feel there should be a way to do this without repeating this for every class... the whole point of class inheretance:

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)
5
  • You should switch to version 3.6... I guess I have never seen dunder init in a metaclass but I am certainly inexperienced. Where have you come across structuring things this way? Commented Dec 14, 2017 at 14:32
  • Not sure... I haven't done this kind of deep inheretance in years, and it was in C++. Bottom line... a Row object, like a recordclass object, should be standard... meaning I shouldn't have to repeat any code common to this Row concept in each iteration of the row just because the row data changed. Commented Dec 14, 2017 at 14:36
  • 1
    afaik metaclasses are not really about inheritance or subclassing. metaclasses can be used to control making a class. You should seach for videos for python metaclass and watch some - Pycon videos at pyvideo.org are usually pretty good. Commented Dec 14, 2017 at 19:16
  • Also, what is this hate for "non ASCII text" about? Other characters are deterministic, and just by using explicit encodings, you can have your program working with data from the real world (with exotic words like "resumé" and such). I'd suggest reading the following article. Please don't be offended or scared by it's title joelonsoftware.com/2003/10/08/… Commented Dec 14, 2017 at 20:04
  • This doesn't look like a use-case for a metaclass... Commented Dec 14, 2017 at 20:06

1 Answer 1

2

What you need is just ordinary class inheritance, and maybe an ordinary function to work as a factory for your classes.

Since the only thing you need is the ordered list of the keys, that gets present in the classes' slots, that is just it - you can build a base class with the code you already have, and simply inherit from it for your class structures.

If you want the full functionality of a mapping, like dict, being able to retrieve elements, get the length, and so on, I'd recommend inheriting from collections.abc.MutableMapping instead of OrderedDict. Even because, if you inherit form OrderedDict, the __slots__ declaration will be worthless - dicts and OrderedDict keep their data arranged in ways that are not acessible through Python code - you can keep your code solely in each class __slots__.

Also, collections.abc.MutableMapping is crafted in a way that it requires you to implement a minimum set of methods, from which it derives all functionality from a dict.

So, adapting your last example class, you'd have something like this.

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

And this is it working:

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'}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.