Use dynamic inherit classes to specify characteristic of an object - python

I have an object that encapsulate a network profile that have some characteristics. For example, my profile has a Connection and depending on it, it could or couldn't have an IP option (static or dhcp).
So, my first attempt was using a normal class that extends from dict and add some helper functions:
class Profile(dict):
IP_CONNECTIONS = ('ethernet', 'wireless', 'pppoe')
def is_ethernet(self): return self['Connection'] == 'ethernet'
def is_wireless(self): return self['Connection'] == 'wireless'
def is_wireless_adhoc(self): return self.is_wireless() and 'AdHoc' in self
def has_ip(self)
return self['Connection'] in self.IP_CONNECTIONS
def has_ip_static(self)
if not self.has_ip():
return False
if self.is_ipv4():
return self['IP'] == 'static'
if self.is_ipv6():
return self['IP6'] == 'static'
return False
def has_ip_dhcp(self):
if not self.has_ip():
return False
if self.is_ipv4():
return self['IP'] == 'dhcp'
if self.is_ipv6():
return self['IP6'] == 'dhcp' or self['IP6'] == 'dhcp-noaddr'
return False
def is_ipv4(self): return self.has_ip() and 'IP' in self
def is_ipv6(self): return self.has_ip() and 'IP6' in self
def get_client(self):
if self.has_ip_dhcp() and 'DHCPClient' in self:
return self['DHCPClient']
return None
This worked, but I had a enormous class with a lot of is_* and has_* characteristic functions. Most of them would be only used for a very specific profile, and return False most of the time.
Then it crossed my mind that I can use inheritance to describe characteristics.
After trying and failed to implement a metaclass because the data was not yet available when the __new__ method was called. I came up with something like this:
def load_profile(filename):
data = _read_profile(filename)
bases = _classify_profile(data)
baseclass = type('Profile', bases, {})
return baseclass(data)
class IP:
CONNECTIONS = ('ethernet', 'wireless')
class IPv4(IP):
def is_static(self):
return self['IP'] == 'static'
class IPv6(IP):
def is_static(self):
return self['IP6'] == 'static'
class DHCP:
def get_client(self):
return self['DHCPClient'] if 'DHCPClient' in self else None
class Wireless:
def is_adhoc(self):
return 'AdHoc' in self
def _classify_profile(data):
classes = [dict]
if data['Connection'] == 'wireless':
classes.append(Wireless)
if data['Connection'] in IP.CONNECTIONS:
if 'IP' in data:
classes.append(IPv4)
if data['IP'] == 'dhcp':
classes.append(DHCP)
if 'IP6' in data:
classes.append(IPv6)
if data['IP6'] == 'dhcp' or data['IP6'] == 'dhcp-noaddr':
classes.append(DHCP)
return tuple(classes)
When before I was doing profile.has_ip(), now I just test it with isinstance(profile, IP). This seems to me more clear with good separation of responsibility.
Question: Is this a good way of implementing dynamic inheritance? What would be the pythonic way of doing this?
Thanks in advance!

I do not really know what you mean by dynamic inheritance but I would write it this way:
base_classes = []
class IP(dict):
CONNECTIONS = ('ethernet', 'wireless')
def is_static(self):
raise NotImplementedError('To be implemented in subclasses.')
#classmethod
def wants_the_meaningful_named_data(cls, data):
return False
base_classes.append(IP)
class IPv4(IP):
def is_static(self):
return self['IP'] == 'static'
#classmethod
def wants_the_meaningful_named_data(cls, data):
return data['Connection'] in cls.CONNECTIONS and 'IP' in data
base_classes.append(IPv4)
class IPv6(IP):
def is_static(self):
return self['IP6'] == 'static'
#classmethod
def wants_the_meaningful_named_data(cls, data):
return data['Connection'] in cls.CONNECTIONS and 'IP6' in data
base_classes.append(IPv6)
def load_profile(filename):
data = _read_profile(filename)
for base_class in base_classes:
if base_class.wants_the_meaningful_named_data(data):
return base_class(data)
return dict(data)
Something like this would be what I like. I do not see the need to go into metaclasses.

Related

Method declared but not found in a class object

I just began the long and painful journey in Python Class Objects and try this:
class UgcObject:
def __init__(self, strPlateformeOld, strIdOld):
self.strPlateformeOld = strPlateformeOld
self.strIdOld = strIdOld
def GetApiUpdatedMetadata(self):
if self.strPlateforme == "youtube":
return True
def GetblnUpdatePossible(self):
return GetApiUpdatedMetadata()
if __name__ == '__main__':
ugc_test = UgcObject("youtube","id")
print(ugc_test.GetblnUpdatePossible())
I got an error message: NameError: name 'GetApiUpdatedMetadata' is not defined
I don't get why considering that I believe the GetApiUpdatedMetadata is declared and above the method that calls it.
What did I did wrong?
If you are trying to call another method in the same class it should have self. in front of it, and the variable name self.strPlateforme is wrong:
class UgcObject:
def __init__(self, strPlateformeOld, strIdOld):
self.strPlateformeOld = strPlateformeOld
self.strIdOld = strIdOld
def GetApiUpdatedMetadata(self):
if self.strPlateformeOld == "youtube":
return True
def GetblnUpdatePossible(self):
return self.GetApiUpdatedMetadata()
if __name__ == '__main__':
ugc_test = UgcObject("youtube","id")
print(ugc_test.GetblnUpdatePossible())

How to make a polymorphic dataclass constructor method

I have 3 dataclass objects say:
class Message1:
def __init__(a):
...
class Message2:
def __init__(d,e,f):
...
class Message3:
def __init__(g,i):
...
For these 3 messages I want to make a factory type method which can return one of the three objects if it succeeds and if not it should return either the one it identified as the correct message to be created but failed at creation or it should notify the user that it could not create any of the messages. Are there any OOP patterns for this?
My initial thought was to do a:
def factory_method(**parameters):
try:
Message1(**parameters)
except TypeError:
try:
Message2(**parameters)
except:
try:
Message3(**parameters)
except:
print("Could not deduce message type")
My issue with this idea is that:
It's not a dynamically scalable solution, with each new message class I introduce I need to add a new try catch block
If the whole nested block structure fails, I have no feedback as to why, was the parameters correct for one of the message but wrong value, or was it plain gibberish?
I realize this might be a bit opinion based on what the best outcome is. At the same time it might be the solution is not too elegant and the simplest way is to just tell the factory_method what kind of message to initialize. Any suggestions or ideas would be appreciated.
If you can't join them all in a single class and you can't point a call to a single class, i would match the arguments to the posible class. To make it work a type hint and a "proxy" class is required. This example asumes that any of the classes wont contain a __init__(*args, **kwargs), and to add a new class you just add it to Message.msg_cls, you can eval the global scope if you don't want to add manually each class.
class Message1:
def __init__(self, a: int, alt=None, num=10):
print('Message 1')
class Message2:
def __init__(self, d: str, e: str, f: int):
print('Message 2')
class Message3:
def __init__(self, g: int, i: any):
print('Message 3')
class Message:
msg_cls = (
Message1,
Message2,
Message3
)
#staticmethod
def eq_kwargs(cls, kwargs):
cls_kwargs = cls.__init__.__defaults__
if cls_kwargs is None:
if len(kwargs) > 0:
return False
else:
return True
cls_astr = cls.__init__.__code__
kw_types = [type(t) for t in cls_kwargs]
for k in kwargs:
if k in cls_astr.co_varnames:
if type(kwargs[k]) in kw_types:
kw_types.remove(type(kwargs[k]))
else:
if type(None) in kw_types:
kw_types.remove(type(None))
else:
return False
else:
return False
return True
#staticmethod
def eq_args(cls, args):
cls_args = cls.__init__.__annotations__
if len(cls_args) != len(args):
return False
for a, b in zip(args, cls_args):
if type(a) != cls_args[b] and cls_args[b] != any:
return False
return True
def __new__(cls, *args, **kwargs):
for mc in Message.msg_cls:
if Message.eq_args(mc, args):
if Message.eq_kwargs(mc, kwargs):
return mc(*args, **kwargs)
raise ValueError('Message.__new__, no match')
if __name__ == '__main__':
ms_1_a = Message(1, alt='a')
ms_1_b = Message(2, alt='a', num=5)
ms_2 = Message('X', 'Y', 5)
ms_3_a = Message(1, [1, 4])
ms_3_b = Message(2, Message(10))

dynamically creating classes with dynamically created methods()

I'm trying to create a dynamic class in python that also has dynamic properties; but I'm struggling with this.
Here's a simple non-working sample:
class baseA(object):
def __init__(self):
self._a_info = 1
def dump(self, pref=""):
print("%s %d" % (pref, self._a_info))
def b_init(self, parent_class):
parent_class.__init__(self)
def _dump(self, pref=""):
print("%s: %d" % self._b_info)
attrs = {"__init__": b_init,
"dump": lambda self, pref: _dump(self, pref=pref)}
for field in ["field1", "field2"]:
attrs["_%s" % field] = field
attrs[field] = lambda self: getattr(self, "_%s" % f)
tmpb = type("baseB",
(baseA, ),
attrs)
t = tmpb()
t.dump(pref="Field: ")
Obviously, the above doesn't work. For one thing print(t.field1) will print an unbounded method warning, since attrs[prop] is a function and not a value. (I was hoping to simulate
what #property does to methods).
What I'm trying to do is to create a class dynamically while setting properties
for it. That said, I now realize that "attrs[prop] = lambda self: getattr(self, "_%s" % prop)
is wrong as that makes attrs[prop] a function.
Is it even possible to use the type() function to create a dynamic class that has
the following property getter/setters?
So like converting the following:
class baseB(baseA):
def __init__(self):
self._field1 = "field1"
self._field2 = "field2"
self._field3 = "field3"
#property
def field1(self):
return self._field1
#field1.setter
def field1(self, in_val):
self._field1 = in_val
#property
def field2(self):
return self._field2
#field2.setter
def field2(self, in_val):
self._field2 = in_val
#property
def field3(self):
return self._field3
#field3.setter
def field3(self, in_val):
self._field3 = in_val
to
type("baseB",
(baseA, ),
someattributes_dictionary)
?
If it was a one off script, then sure I'd just do the long way; but if I need
to dynamically create different classes with different properties, the typical
'class ...' will be tedious and cumbersome.
Any clarifications appreciated,
Ed.
--
[1] - https://www.python-course.eu/python3_classes_and_type.php

Python main function in class

I'm new to python and the main() method and class def's are confusing me. I'm trying to create a bloom filter and my program keeps terminating because I don't think I'm calling things correctly.
class BloomFilter(object):
def __init__(self, numBits, numHashFunctions):
self.numBits = numBits
self.bitArray = [0] * numBits
self.hash = bloomFilterHash(numBits, numHashFunctions)
def insert(self, key):
def lookup(self, key):
def rand_inserts(self,num):
def main(): #not sure if i should put this inside or outside class
bloomfilter = BloomFilter(100,5)
bloomfilter.rand_inserts(15)
if __name__ == '__main__':
BloomFilter().main()
So if I wanted to create a bloom filter with 100 numBits and 5 hash functions, should i call that under the if __name__ == '__main__' or under def main()? I'm not sure if I'm calling these correctly as I'm much more familiar with Java. thanks!
def main():
bloomfilter = BloomFilter(100,5)
bloomfilter.rand_inserts(15)
the name == '__main__' clause is to make sure your code only runs when the module is called directly, not, for instance, if you are importing something from the module in another module. main() is not a special method for a python class, so I believe your objective here, in a simplified way, is the following:
class BloomFilter(object):
def __init__(self, numBits, numHashFunctions):
self.numBits = numBits
self.bitArray = [0] * numBits
self.hash = bloomFilterHash(numBits, numHashFunctions)
if __name__ == '__main__':
# creates an instance of the class
bloomfilter = BloomFilter(100,5)
# apply some method to instance...
bloomfilter.rand_inserts(15)
You would want to put main() outside the class:
class BloomFilter(object):
def __init__(self, numBits, numHashFunctions):
self.numBits = numBits
self.bitArray = [0] * numBits
self.hash = bloomFilterHash(numBits, numHashFunctions)
def insert(self, key):
def lookup(self, key):
def rand_inserts(self,num):
def main():
some_value = Bloomfilter(100, 5)
some_value.rand_inserts(15)
main()

#register.filter in my code

from django import template
register = template.Library()
class_converter = {
"textinput":"textinput textInput",
"fileinput":"fileinput fileUpload"
}
#register.filter#<--------
def is_checkbox(field):
return field.field.widget.__class__.__name__.lower() == "checkboxinput"
#register.filter#<--------
def with_class(field):
class_name = field.field.widget.__class__.__name__.lower()
class_name = class_converter.get(class_name, class_name)
if "class" in field.field.widget.attrs:
field.field.widget.attrs['class'] += " %s" % class_name
else:
field.field.widget.attrs['class'] = class_name
return unicode(field)
and register.filter function is:
def filter(self, name=None, filter_func=None):
if name == None and filter_func == None:
# #register.filter()
return self.filter_function
elif filter_func == None:
if(callable(name)):
# #register.filter
return self.filter_function(name)
else:
# #register.filter('somename') or #register.filter(name='somename')
def dec(func):
return self.filter(name, func)
return dec
elif name != None and filter_func != None:
# register.filter('somename', somefunc)
self.filters[name] = filter_func
return filter_func
else:
raise InvalidTemplateLibrary("Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func))
so
#register.filter
def a():
pass
is Equal
register.filter(name=None,filter_func=a)
yes??
Not exactly. The decorator syntax:
#register.filter
def a():
pass
is syntactic sugar for:
def a():
pass
a = register.filter(a)
So register.filter in this case will be called with the first positional argument, 'name' being your function. The django register.filter function handles that usage however and returns the right thing even if the filter is sent as the first argument (see the if callable(name) branch)
It's more common to make decorators that can take multiple arguments do so with the function to be decorated being the first positional argument (or alternately being function factories/closures), but I have a feeling the reason django did it this way was for backwards-compatibility. Actually I vaguely remember it not being a decorator in the past, and then becoming a decorator in a later django version.
No. Simple decorators take the function they decorate as a parameter, and return a new function.
a = register.filter(a)

Categories

Resources