Python class __call__ method and dot notation - python

My Goal is to use dot notation to select strings from dictionarys using the SimpleNamespace modeule while having the ability to change which dictionary to use.
To do this i have tried modifying the class __call__ method to change the output based on a previously set variable. However, due to the use of the __call__ method it requires the use of () to be included which breaks the simple formatting of dot notation. Additinally i need to be able to use class methods as well to change the option i am looking for.
class i: x, y = 1, 2
class j: x, y = 3, 4
class myClass:
def __init__(self):
self.a, self.b = i(), j()
self.selection = "a"
def set_selection(self, selection):
self.selection = selection
def __call__(self):
return getattr(self, self.selection)
mc = myClass()
print(mc().x) ## this generates the output i am wanting by using the __call__ method
mc.set_selection("b") ## i still need to call class methods
print(mc().x)
print(mc.x) ## this is the syntax i am trying to achive
although mc().x works it is not dot notation.
The output i am looking for in this example would be similar to:
import myClass
data = myCalss()
print(data.x + data.y)
#>>> 3
data.set_selection("b")
print(data.x + data.y)
#>>> 7

Seem like __call__() is the wrong choice for the interface you want. Instead, maybe __getattr__() is what you want:
class i: x, y = 1, 2
class j: x, y = 3, 4
class myClass:
def __init__(self):
self.a, self.b = i(), j()
self.selection = "a"
def set_selection(self, selection):
self.selection = selection
def __getattr__(self, at):
return getattr(getattr(self, self.selection), at)
data = myClass()
print(data.x + data.y)
# 3
data.set_selection("b")
print(data.x + data.y)
# 7
Might want some checks to make sure the selection is valid.
Also, probably worth reading up on descriptors if you will be exploring this kind of stuff more deeply.

Related

Automatically extend class with methods coming from another module

Immagine that I have defined several methods acting on an object, and I have two or more different classes that cannot inherit from the same parent, having an instance of that object. I want to automatically add all the methods to the two classes, removing the first argument (the object) and replacing it with the instance owned by the class.
Is there a way to do it?
I am sure my question is not clear, so I try to give a super simplified settings. to keep things simple the object is just a list. I hope that after the example my objective is clear! Thanks in advance for your time.
# I define some methods acting on an object (just 2 useless methods acting on a list in this example)
def get_avg(input_list):
return sum(input_list) / len(input_list)
def multiply_elements(input_list, factor):
return [i * factor for i in input_list]
Then we have 2 different classes, both have an instance of our object (the list)
class A:
list_of_apples = []
def get_list_of_apples(self):
return self.list_of_apples
class B:
"""Totally different class from A(pples), also containing a list"""
list_of_bears = []
def get_list_of_bears(self):
return self.list_of_bears
Now, to call a "list" method on the lists owned by A and B instances, I would need to do the following:
b = B()
get_avg(b.get_list_of_bears())
My goal, instead, is to automatically define some wrappers (as the following ones) which would allow me to call list methods directly from instances of A and B. Here there is an example for B:
class B:
"""Totally different class from A(pples), but containing a list"""
list_of_bears = []
def get_list_of_bears(self):
return self.list_of_bears
def get_avg(self):
return get_avg(self.list_of_bears)
def multiply_elements(self, factor):
return multiply_elements(self.list_of_bears, factor)
With the extended class, I can simply do:
b = B()
b.get_avg()
b.multiply_elements(factor=10)
I would like to automatically extend A and B.
I don't know why your classes cannot inherit from a common ancestor but one solution I can think of is to make the ancestor dynamically:
def make_ancestor():
class Temp:
def get_avg(self):
input_list = getattr(self, self.list_name)
return sum(input_list) / len(input_list)
def multiply_elements(self, factor):
input_list = getattr(self, self.list_name)
return [i * factor for i in input_list]
return Temp
class A(make_ancestor()):
list_of_apples = []
list_name = 'list_of_apples'
def get_list_of_apples(self):
return self.list_of_apples
class B(make_ancestor()):
list_of_bears = []
list_name = 'list_of_bears'
def get_list_of_bears(self):
return self.list_of_bears
Now since the parent classes are being generated dynamically your child classes don't inherit from the same parent.
As a test:
print(make_ancestor() == make_ancestor()) # False

Polymorphism and Overriding in Python

I have two classes: A and B. I would like to build a class C which is able to overrides some common methods of A and B.
The methods which I would like to override they should be able to call the method of the base class.
In practice I would like to collect some statistics about class A and B, but being transparent to the rest of my code. Now, A and B have some methods in common (obviously implemented in a different way). I would like to have a class C which shares the interface of A and B, and simoultaneously do some other operations (i.e. measure the run time of some shared methods).
I can make this example:
import time
class A:
def __init__(self):
pass
def common_method(self):
return "A"
class B:
def __init__(self):
pass
def common_method(self):
return "B"
class C:
def __init__(self, my_obj):
self.my_obj
self.time_avg = 0
self.alpha = 0.1
pass
def common_method(self):
start = time.time()
ret = self.my_obj.common_method()
stop = time.time()
self.time_avg = (1. - self.alpha) * self.time_avg + self.alpha * (stop - start)
return ret
I hope that from this example is clear that A and B inheriting from C is not working.
However, this method unfortunately require me to redefine all the methods of classes A and B... Which is tedious and dirty!
What is the proper way to implement this situation in python? And how it is called the design pattern (I am almost sure that there is but I cannot recall).
Thanks in advance.
You could solve this with composition instead of polymorphism, meaning that a C object will hold either a A object or a B one:
class C:
def __init__(self, obj):
self._obj = obj
def common_method(self):
return self._obj.common_method()
You can then use it:
>>> ca = C(A())
>>> cb = C(B())
>>> ca.common_method()
'A'
>>> cb.common_method()
'B'
Beware: if you pass an object that does not declare a common_method method, you will get an AttributeError

Python class instances unique by some property

Suppose I want to create instances of my class freely, but if I instantiate with the same argument, I want to get the same unique instance representing that argument. For example:
a = MyClass('Instance 1');
b = MyClass('Instance 2');
c = MyClass('Instance 1');
I would want a == c to be True, based on the unique identifier I passed in.
Note:
(1) I'm not talking about manipulating the equality operator-- I want a to really be the same instance as c.
(2) This is intended as library code, so uniqueness has to be enforced-- we can't just count on users doing the right thing (whatever that is).
Is there a canonical way of achieving this? I run into this pattern all the time, but I usually see solutions involving shadow classes, meant for only internal instantiation. I think I have a cleaner solution, but it does involve a get() method, and I'm wondering if I can do better.
I'd use a metaclass. This solution avoids calling __init__() too many times:
class CachedInstance(type):
_instances = {}
def __call__(cls, *args):
index = cls, args
if index not in cls._instances:
cls._instances[index] = super(CachedInstance, cls).__call__(*args)
return cls._instances[index]
class MyClass(metaclass=CachedInstance):
def __init__(self, name):
self.name = name
a = MyClass('Instance 1');
b = MyClass('Instance 2');
c = MyClass('Instance 1');
assert a is c
assert a is not b
Reference and detailed explanation: https://stackoverflow.com/a/6798042/8747
This can be done (assuming that args are all hashable)
class MyClass:
instances = {}
def __new__(cls, *args):
if args in cls.instances:
return cls.instances[args]
self = super().__new__(cls)
cls.instances[args] = self
return self
a = MyClass('hello')
b = MyClass('hello')
c = MyClass('world')
a is b and a == b and a is not c and a != c # True
is is the python operator that shows two objects are the same instance. == falls back to is on objects where it is not overidden.
As pointed out in the comments, this can be a bit troubling if you have an __init__ with side effects. Here's an implementation that avoids that:
class Coord:
num_unique_instances = 0
_instances = {}
def __new__(cls, x, y):
if (x, y) in cls._instances:
return cls._instances[x, y]
self = super().__new__(cls)
# __init__ logic goes here -- will only run once
self.x = x
self.y = y
cls.num_unique_instances += 1
# __init__ logic ends here
cls._instances[x, y] = self
return self
# no __init__ method

Is it possible to define an integer-like object in Python that can also store instance variables?

Is it possible to define a data object in python that behaves like a normal integer when used in mathematical operations or comparisons, but is also able to store instance variables?
In other words, it should be possible to do the following things:
pseudo_integer = PseudoInteger(5, hidden_object="Hello World!")
print(5 + pseudo_integer) # Prints "10"
print(pseudo_integer == 5) # Prints "True"
print(pseudo_integer.hidden_object) # Prints "Hello World!"
So, all answers above are fine, but probably you don't want to re-define all existing methods.
Normally, in python you can just subclass any built-in class (type). But with immutable types (and integers in python are immutable) is slightly tricky. TL;DR:
class PseudoInt(int):
def __new__(cls, x, hidden, *args, **kwargs):
instance = int.__new__(cls, x, *args, **kwargs)
instance.hidden = hidden
return instance
x = PseudoInt(5, 'secret')
x.hidden # 'secret'
x + 4 # 9
x * 3 # 15
Normally, you should reload __init__ method, but with immutable bases you should use __new__. You can read more about data model in corresponding docs section
All this is viable only if you need single signature for constructing your object. If its fine to have 1 call for creating, and dedicated calls to populate object with attributes - Kevin's answer is all you need
Yes, it is. You can create your own custom class. Python has many magic methods to help you archive that.
Check the code:
class PseudoInteger:
def __init__(self, x, s):
self.x = x
self.s = s
def __add__(self, num):
return self.x + num
def __eq__(self, num):
return self.x == num
a = PseudoInteger(5, 'hello, world')
print(a + 3)
print(a == 5)
print(a == 2)
Or you can just inherit from int, after creating an instance, you are able to assign attributes to the inherited int object. You can't assign attributes to int directly, because int does not support item assignment :
class PseudoInteger(int):
pass
a = PseudoInteger(5)
a.hidden = 'hello, world'
print(a)
print(a == 5)
print(a + 3)
print(a.hidden)
You simply need a class for this:
class PseudoInteger(object):
def __init__(self, num, hidden=None):
self.num = num
self.hidden = hidden
def __add__(self, otherVal):
if isinstance(otherVal, PseudoInteger):
return self.num + otherVal.num
else:
return self.num + otherVal
p = PseudoInteger(4, 'Tiger')
q = PseudoInteger(6, 'lion')
print (p+q)
print (p+4)
This prints out:
10
8
You have to add the other operations (division, substraction, eq, ...) you need to the class on your own :)
Look into implementing the __add__ and __eq__ methods for your PseudoInteger class

Apply a function to all instances of a class

I am looking for a way to apply a function to all instances of a class. An example:
class my_class:
def __init__(self, number):
self.my_value = number
self.double = number * 2
#staticmethod
def crunch_all():
# pseudocode starts here
for instances in my_class:
instance.new_value = instance.my_value + 1
So the command my_class.crunch_all() should add a new attribute new_value to all existing instances. I am guessing I will have to use #staticmethod to make it a "global" function.
I know I could keep track of the instances that are being defined by adding something like my_class.instances.append(number) in __init__ and then loop through my_class.instances, but I had no luck so far with that either. Also I am wondering if something more generic exists. Is this even possible?
Register objects with the class at initialisation (i.e. __init__) and define a class method (i.e. #classmethod) for the class:
class Foo(object):
objs = [] # registrar
def __init__(self, num):
# register the new object with the class
Foo.objs.append(self)
self.my_value = num
#classmethod
def crunch_all(cls):
for obj in cls.objs:
obj.new_value = obj.my_value + 1
example:
>>> a, b = Foo(5), Foo(7)
>>> Foo.crunch_all()
>>> a.new_value
6
>>> b.new_value
8

Categories