Is it possible to have python functions with "inheritance"? - python

Im trying to create a "function with inheritance" so that I dont have to have a bunch of similar functions with copy pasted code. And this is effectively what I ended up with:
class TypeConverter:
__metaclass__ = ABCMeta
def __convert__(self, thing):
if type(thing) == list:
return [self.convert_one(self,t) for t in thing]
else:
return self.convert_one(self,thing)
def __new__(self, thing):
return self.__convert__(self,thing)
#abstractmethod
def convert_one(self, thing):
pass
class HexToInt(TypeConverter):
def convert_one(self, _hex):
return int(_hex, 16)
class IntToHex(TypeConverter):
def convert_one(self, _int):
return hex(_int)
In py3 it runs fine and the objects work like functions which is the intent:
>>> HexToInt(['ff' , 'fe'])
[255, 254]
>>> IntToHex(255)
'0xff'
In py2 it does not work (but thats where I need it to work):
unbound method __convert__() must be called with HexToInt instance as first argument (got ABCMeta instance instead)
Ideally TypeConverter can hold all the some logic about whether or not to return a sequence and what type of sequence it should be etc, and the child classes implement implement some other specific logic. I dont want to have have regular function objects since id have to instantiate and then call on the object I want it to work exactly like a function; i.e does the thing when the parans close,like in the example above.
How bad of an idea is this? What are the problems here? Can I make it work even if I shouldn't?
Any and all thoughts highly appreciated.
Update, it works:
class TypeConverter(object):
def __convert(this, thing):
if type(thing) == list:
return [this.convert_one(t) for t in thing]
else:
return this.convert_one( thing)
def __new__(cls, thing):
c = super(TypeConverter, cls).__new__(cls)
return cls.__convert(c, thing)
#abstractmethod
def convert_one(self, thing):
pass
class HexToInt(TypeConverter):
def convert_one(self, _hex):
return int(_hex, 16)
class IntToHex(TypeConverter):
def convert_one(self, _int):
return hex(_int)

This would be a lot simpler as a simple decorator:
def one_or_many(f):
def wrapper(arg):
if isinstance(arg, list):
return list(map(f, arg))
else:
return f(arg)
return wrapper
#one_or_many
def hex_to_int(_hex):
return int(_hex, 16)
int_to_hex = one_or_many(hex)
No need for classes if you don't actually need classes.

Related

Python Expert: how to inherit built-in class and override every member function w.r.t. the base-class member function?

It is known that in Python, due to optimization concerns, we cannot add/modify member functions of a built-in class, e.g., adding an sed function to the built-in str class to perform re.sub(). Thus, the only way to achieve so is to inherit the class (or subclassing). i.e.,
class String(str):
def __init__(self, value='', **kwargs):
super().__init__()
def sed(self, src, tgt):
return String(re.sub(src, tgt, self))
The problem with this is that after sub-classing, member functions return base-class instance instead of the inherited class instance. For example, I would like to chain String edits String(' A b C d E [!] ').sed(...).lower().sed(...).strip().sed('\[.*\]', '').split() and so on. However, functions such as .lower() and .strip() returns an str instead of String, so cannot perform .sed(...) afterwards. And I do not want to keep casting to String after every function call.
So I did a manual over-ride of every base-class methods as follows:
class String(str):
for func in dir(str):
if not func.startswith('_'):
exec(f'{func}=lambda *args, **kwargs: [(String(i) if type(i)==str else i) for i in [str.{func}(*args, **kwargs)]][0]')
def __init__(self, value='', **kwargs):
super().__init__()
def sed(self, src, tgt):
return String(re.sub(src, tgt, self))
However, not every member function returns a simple str object, e.g., for functions such as .split(), they return a list of str; other functions like .isalpha() or .find() return boolean or integer. In general, I want to add more string-morphing functions and do not want to manually over-ride member functions of each return type in order to return inherited-class objects rather than base-class objects. So is there a more elegant way of doing this? Thanks!
Python's built-in classes are not designed to support that style of inheritance
easily. Also, the whole idea seems flawed to my eye. Even if you do figure out
a way to solve the problem as you've framed it, what's the advantage over good
old functions?
# Special String objects with new methods.
s = String('foo bar')
result = s.sed('...', '...')
# Regular str instances passed to ordinary functions.
s = 'foo bar'
result = sed(s, '...', '...')
That said, here's one way to try. I have not tested it
extensively, it might have a flaw, and I would never use it in real code.
The basic idea is to capture objects returned during low-level
attribute access, and if the object is callable return
a wrapped version of it that will perform the needed
data conversions.
import re
from functools import wraps
class String(str):
def __getattribute__(self, attr):
obj = object.__getattribute__(self, attr)
return wrapped(obj) if callable(obj) else obj
def __init__(self, value='', **kwargs):
super().__init__()
def sed(self, src, tgt):
return re.sub(src, tgt, self)
def wrapped(func):
#wraps(func)
def wrapper(*xs, **kws):
obj = func(*xs, **kws)
return convert(obj)
return wrapper
def convert(obj):
if isinstance(obj, str):
return String(obj)
elif isinstance(obj, list):
return [convert(x) for x in obj]
elif isinstance(obj, tuple):
return tuple(convert(x) for x in obj)
else:
return obj
Demo:
s = String('foo bar')
got = s.sed('foo', 'bzz').upper().split()
print(got)
print(type(got))
print(type(got[0]))
Output:
['BZZ', 'BAR']
<class 'list'>
<class '__main__.String'>

How can I return self and another variable in a python class method while method chaining?

I understand what I am asking here is probably not the best code design, but the reason for me asking is strictly academic. I am trying to understand how to make this concept work.
Typically, I will return self from a class method so that the following methods can be chained together. My understanding is by returning self, I am simply returning an instance of the class, for the following methods to work on.
But in this case, I am trying to figure out how to return both self and another value from the method. The idea is if I do not want to chain, or I do not call any class attributes, I want to retrieve the data from the method being called.
Consider this example:
class Test(object):
def __init__(self):
self.hold = None
def methoda(self):
self.hold = 'lol'
return self, 'lol'
def newmethod(self):
self.hold = self.hold * 2
return self, 2
t = Test()
t.methoda().newmethod()
print(t.hold)
In this case, I will get an AttributeError: 'tuple' object has no attribute 'newmethod' which is to be expected because the methoda method is returning a tuple which does not have any methods or attributes called newmethod.
My question is not about unpacking multiple returns, but more about how can I continue to chain methods when the preceding methods are returning multiple values. I also understand that I can control the methods return with an argument to it, but that is not what I am trying to do.
As mentioned previously, I do realize this is probably a bad question, and I am happy to delete the post if the question doesnt make any sense.
Following the suggestion by #JohnColeman, you can return a special tuple with attribute lookup delegated to your object if it is not a normal tuple attribute. That way it acts like a normal tuple except when you are chaining methods.
You can implement this as follows:
class ChainResult(tuple):
def __new__(cls, *args):
return super(ChainResult, cls).__new__(cls, args)
def __getattribute__(self, name):
try:
return getattr(super(), name)
except AttributeError:
return getattr(super().__getitem__(0), name)
class Test(object):
def __init__(self):
self.hold = None
def methoda(self):
self.hold = 'lol'
return ChainResult(self, 'lol')
def newmethod(self):
self.hold = self.hold * 2
return ChainResult(self, 2)
Testing:
>>> t = Test()
>>> t.methoda().newmethod()
>>> print(t.hold)
lollol
The returned result does indeed act as a tuple:
>>> t, res = t.methoda().newmethod()
>>> print(res)
2
>>> print(isinstance(t.methoda().newmethod(), tuple))
True
You could imagine all sorts of semantics with this, such as forwarding the returned values to the next method in the chain using closure:
class ChainResult(tuple):
def __new__(cls, *args):
return super(ChainResult, cls).__new__(cls, args)
def __getattribute__(self, name):
try:
return getattr(super(), name)
except AttributeError:
attr = getattr(super().__getitem__(0), name)
if callable(attr):
chain_results = super().__getitem__(slice(1, None))
return lambda *args, **kw: attr(*(chain_results+args), **kw)
else:
return attr
For example,
class Test:
...
def methodb(self, *args):
print(*args)
would produce
>>> t = Test()
>>> t.methoda().methodb('catz')
lol catz
It would be nice if you could make ChainResults invisible. You can almost do it by initializing the tuple base class with the normal results and saving your object in a separate attribute used only for chaining. Then use a class decorator that wraps every method with ChainResults(self, self.method(*args, **kw)). It will work okay for methods that return a tuple but a single value return will act like a length 1 tuple, so you will need something like obj.method()[0] or result, = obj.method() to work with it. I played a bit with delegating to tuple for a multiple return or to the value itself for a single return; maybe it could be made to work but it introduces so many ambiguities that I doubt it could work well.

What would be better way if any to store list of methods that would be easily accessible?

I have a class that has multiple methods and I want to store all of the available methods that would be easily accessible in example would be something like this
class Methods:
def foo(self, a):
return f'hello {a}'
def bar(self, b):
return f'hello {b}'
def methods_dict(self, var, **kwargs):
dic = {
'me' : self.foo(var),
'be': self.bar(var)
}
return dic
But on runtime my methods_dict() method will execute both of the methods inside of it's dictionary.
One one hand I'm planing to store only strings in there and it's really easily accessible, on the other hand i probably would not need to access all of the available methods at once.
Any suggestions ?
I am planning to use those methods as follows
class InheritMethods(Methods):
def __init__(self, method_name):
self.method_name = method_name
def add_to_list(self, input):
arr = []
arr.append(self.method_dicts(input)[self.method_name]
return arr
To clear things up, I am gonna call specific method based on input name, so basically input == method_name
I could do conditional statements like if input == 'foo': do somethings.., but if i end up having a lot of methods, my code is going to be a mess, so i assume(!) that would not be a great idea
I think you can get what you want with the following. Your exact usecase is still not clear. Please respond if I am heading in the wrong direction.
Using self.__getattribute__() you can get a function by name. Of course you would have to catch exceptions etc.
class Methods:
def foo(self, a):
return f'hello {a}'
def bar(self, b):
return f'hello {b}'
class InheritMethods(Methods):
def __init__(self, method_name):
self.method_name = method_name
def add_to_list(self, method_name, input):
method = getattr(self, method_name)
result = method(input)
return [result]
class InheritSingleMethod(Methods):
def __init__(self, method_name):
self.add_to_list = self.getattr(self, method_name)
Output
# Any method version
inherit_methods = InheritMethods('a') # < no use for that argument right?
inherit_methods.add_to_list('foo', 'laurens')
> ['hello laurens']
# Single method version
inherit_single_method = InheritSingleMethod('foo')
inherit_single_method.add_to_list('laurens')
> 'hello laurens'
If all you want to do is access a method of Methods given the name in a str, use getattr:
name = input()
m = Methods()
getattr(m, name)("bob")

Operators for new-style classes

Could anyone explain to me why A()+A() does give an error, but B()+B() works as expected? I came across this when I was writing a larger piece of code, but this seems to be the smallest code necessary to reproduce it.
from types import MethodType
D = {'__add__': lambda x, y: "".join((repr(x), repr(y)))}
class A(object):
def __getattr__(self, item):
if item == '__coerce__':
raise AttributeError()
return MethodType(D[item], self)
def __repr__(self):
return "A"
class B():
def __getattr__(self, item):
if item == '__coerce__':
raise AttributeError()
return MethodType(D[item], self)
def __repr__(self):
return "B"
try:
A()+A()
except Exception as e:
print e
B()+B()
Does anyone have an explanation?
That's because new style classes never invoke __coerce__ with binary operators. Also for special methods __getattr__ is never invoked in new style classes: From Data model coercion rules:
New-style classes (those derived from object) never invoke the
__coerce__() method in response to a binary operator; the only time __coerce__() is invoked is when the built-in function coerce() is called.

Type error with classmethod constructors

I'm implementing several constructors using #classobj. I'm not only setting variables, but also calling methods in the new class:
class Example:
def __init__(self):
pass
#classmethod
def constructor1(cls,x,y):
self=cls
self.__x = x
self.__somemethod(self,y)
...
I get the following error:
unbound method __somemethod() must be called with Example instance as
first argument (got classobj instance instead)
How do I resolve this problem?
If you're wanting your class method to be a constructor, you probably want to be creating an instance of the class you get passed in as cls. I suspect you're trying to do that with your self = cls line, but you're not actually creating a new instance because you've neglected to put parentheses. There are some other issues too, but I think that is the key one. Here's a fixed constructor:
#classmethod
def constructor1(cls,x,y):
self=cls() # parentheses added, to make it a call
self.__x = x
self.__somemethod(y) # self is not needed as a parameter here
return self # return the new instance
looks like __somemethod is not a classmethod, but a "normal" method.
And normal methods expect an actual instance as the first parameter, not a class.
And because constructor1 is decorated as a #classmethod, cls is the class itself - which you pass to __somemethod.
That cannot work.
You should reconsider your design approach.
Addendum:
Maybe you meant something like this?
#classmethod
def constructor1(cls, x, y):
newinst = cls()
newinst.__x = x
cls.__somemethod(newinst, y)
That'd be better written as followed, though:
#classmethod
def constructor1(cls, x, y):
newinst = cls()
newinst.__x = x
newinst.__somemethod(y)
actually, I like neighter approach - seems like a codesmell of overcomplexity to me.
This may be a template of what I think you're trying to achieve...
import random
class Something(object):
def __init__(self, value, **kwargs):
self.value = value
for k, v in kwargs.iteritems():
setattr(self, k, v)
#classmethod
def from_iterable(cls, iterable):
return cls(sum(iterable), description='came from from_iterable')
#classmethod
def make_random(cls):
return cls(random.randint(1,1000), is_random=True)
a = Something.from_iterable([1, 2, 3])
b = Something.make_random()
c = Something(56)
for obj in (a, b, c):
print type(obj), obj.value
<class '__main__.Something'> 6
<class '__main__.Something'> 308
<class '__main__.Something'> 56
Thanks to ch3ka's answer and Tim Pietzcker's comment, I found my error: I used the factory method from http://jjinux.blogspot.co.at/2008/11/python-class-methods-make-good.html and forgot the () in the line self=cls(). Now it works just fine:
class Example:
def __init__(self):
pass
#classmethod
def constructor1(cls,x,y):
self=cls()
self.__x = x
self.__somemethod(self,y)
...

Categories