I'm trying to access instance variables of a parent class as class variables in a child class.
The purpose is that the parent class will have a lot of child classes which all need to have the same structure, and a lot of different people will be working with and creating these child classes, without needing to know the inner workings of the parent class.
Here's my example:
class Human(ABC):
def __new__(cls, *args, **kwargs):
cls.human_name = args[0]
cls.source = f'database_{cls.__name__}'.lower()
return super().__new__(cls)
#property
#abstractmethod
def query(self):
pass
class Company:
class Employee(Human):
query = f'SELECT {human_name} FROM {source};'
# these two functions are just for testing and will not be in the final product
def print_something(self):
print(self.human_name)
def print_source(self):
print(self.source)
e = Company.Employee('John')
print(e.human_name)
print(e.query)
e.print_source()
I want to be able to create a child class of parent class Human (structured together in Company) where I only need to define the query variable which should automatically recognise the variables human_name and source.
How would I go about making this as simple as possible? Is this even possible?
Many thanks!
So, you need to actually implement the property.
class Human(ABC):
def __new__(cls, *args, **kwargs):
cls.human_name = args[0]
cls.source = f'database_{cls.__name__}'.lower()
return super().__new__(cls)
#property
#abstractmethod
def query(self):
pass
class Company:
class Employee(Human):
#property
def query(self):
return f'SELECT {self.human_name} FROM {self.source};'
# these two functions are just for testing and will not be in the final product
def print_something(self):
print(self.human_name)
def print_source(self):
print(self.source)
e = Company.Employee('John')
print(e.human_name)
print(e.query)
e.print_source()
Note, however, since __new__ creates class variables... this query will always be the same across instances:
employee1 = Company.Employee('John')
employee2 = Company.Employee('Jack')
print(employee1.query)
print(employee2.query)
will print:
SELECT Jack FROM database_employee;
SELECT Jack FROM database_employee;
Related
I am trying to find a good way for returning a (new) class object in class method that can be extended as well.
I have a class (classA) which has among other methods, a method that returns a new classA object after some processing
class classA:
def __init__(): ...
def methodX(self, **kwargs):
process data
return classA(new params)
Now, I am extending this class to another classB. I need methodX to do the same, but return classB this time, instead of classA
class classB(classA):
def __init__(self, params):
super().__init__(params)
self.newParams = XYZ
def methodX(self, **kwargs):
???
This may be something trivial but I simply cannot figure it out. In the end I dont want to rewrite the methodX each time the class gets extended.
Thank you for your time.
Use the __class__ attribute like this:
class A:
def __init__(self, **kwargs):
self.kwargs = kwargs
def methodX(self, **kwargs):
#do stuff with kwargs
return self.__class__(**kwargs)
def __repr__(self):
return f'{self.__class__}({self.kwargs})'
class B(A):
pass
a = A(foo='bar')
ax = a.methodX(gee='whiz')
b = B(yee='haw')
bx = b.methodX(cool='beans')
print(a)
print(ax)
print(b)
print(bx)
class classA:
def __init__(self, x):
self.x = x
def createNew(self, y):
t = type(self)
return t(y)
class classB(classA):
def __init__(self, params):
super().__init__(params)
a = classA(1)
newA = a.createNew(2)
b = classB(1)
newB = b.createNew(2)
print(type(newB))
# <class '__main__.classB'>
I want to propose what I think is the cleanest approach, albeit similar to existing answers. The problem feels like a good fit for a class method:
class A:
#classmethod
def method_x(cls, **kwargs):
return cls(<init params>)
Using the #classmethod decorator ensures that the first input (traditionally named cls) will refer to the Class to which the method belongs, rather than the instance.
(usually we call the first method input self and this refers to the instance to which the method belongs)
Because cls refers to A, rather than an instance of A, we can call cls() as we would call A().
However, in a class that inherits from A, cls will instead refer to the child class, as required:
class A:
def __init__(self, x):
self.x = x
#classmethod
def make_new(cls, **kwargs):
y = kwargs["y"]
return cls(y) # returns A(y) here
class B(A):
def __init__(self, x):
super().__init__(x)
self.z = 3 * x
inst = B(1).make_new(y=7)
print(inst.x, inst.z)
And now you can expect that print statement to produce 7 21.
That inst.z exists should confirm for you that the make_new call (which was only defined on A and inherited unaltered by B) has indeed made an instance of B.
However, there's something I must point out. Inheriting the unaltered make_new method only works because the __init__ method on B has the same call signature as the method on A. If this weren't the case then the call to cls might have had to be altered.
This can be circumvented by allowing **kwargs on the __init__ method and passing generic **kwargs into cls() in the parent class:
class A:
def __init__(self, **kwargs):
self.x = kwargs["x"]
#classmethod
def make_new(cls, **kwargs):
return cls(**kwargs)
class B(A):
def __init__(self, x, w):
super().__init__(x=x)
self.w = w
inst = B(1,2).make_new(x="spam", w="spam")
print(inst.x, inst.w)
Here we were able to give B a different (more restrictive!) signature.
This illustrates a general principle, which is that parent classes will typically be more abstract/less specific than their children.
It follows that, if you want two classes that substantially share behaviour but which do quite specific different things, it will be better to create three classes: one rather abstract one that defines the behaviour-in-common, and two children that give you the specific behaviours you want.
I know that each instance will inherit that attribute, but I want a function or should I call it a method of that class to return the set of all instances created of that class.
So let's say I created 3 instances and call a method from the last one that will return all the previously created instances as well as the one that I am calling it from.
I was able to achieve it by making a list, but would it be possible to return a set?
Is there some kind of constructor that I am missing for it?
class Bee():
instances = []
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
def __str__(self):
self.instances.append(f"{self.identifier} {self.name}")
return f"{self.identifier} {self.name}"
def get_hive(self):
return self.instances
Normally you would create Hive as a separate class and put the Bees inside. You then have a clear and explicit data structure whose job includes keeping track of all Bees created.
Something like:
class Hive:
def __init__(self):
self.bees = []
def add_bee(self, bee):
self.bees.append(bee)
class Bee:
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
def __str__(self):
return f"Bee({self.name}, {self.identifier})"
def __repr__(self):
return str(self)
# User code example
hive = Hive()
b1 = Bee('My Bee', 0)
b2 = Bee('Some Other Bee', 1)
hive.add_bee(b1)
hive.add_bee(b2)
print(hive.bees) # display all bees inside the hive
class abc():
def xyz(self):
print("Class abc")
class foo(abc):
def xyz(self):
print("class foo")
x = foo()
I want to call xyz() of the parent class, something like;
x.super().xyz()
With single inheritance like this it's easiest in my opinion to call the method through the class, and pass self explicitly:
abc.xyz(x)
Using super to be more generic this would become (though I cannot think of a good use case):
super(type(x), x).xyz()
Which returns a super object that can be thought of as the parent class but with the child as self.
If you want something exactly like your syntax, just provide a super method for your class (your abc class, so everyone inheriting will have it):
def super(self):
return super(type(self), self)
and now x.super().xyz() will work. It will break though if you make a class inheriting from foo, since you will only be able to go one level up (i.e. back to foo).
There is no "through the object" way I know of to access hidden methods.
Just for kicks, here is a more robust version allowing chaining super calls using a dedicated class keeping tracks of super calls:
class Super:
def __init__(self, obj, counter=0):
self.obj = obj
self.counter = counter
def super(self):
return Super(self.obj, self.counter+1)
def __getattr__(self, att):
return getattr(super(type(self.obj).mro()[self.counter], self.obj), att)
class abc():
def xyz(self):
print("Class abc", type(self))
def super(self):
return Super(self)
class foo(abc):
def xyz(self):
print("class foo")
class buzz(foo):
def xyz(self):
print("class buzz")
buzz().super().xyz()
buzz().super().super().xyz()
results in
class foo
Class abc
I have 3 subclasses and 1 parent class that make the children share a common method.
Example:
class Animal:
def communicate():
pass
class Dog(Animal):
def communicate():
bark()
class Cat(Animal):
def communicate():
meow()
I would like to provide an API that instantiates a cat or a dog based on the received string that will be either "cat" or "dog" and calls .communicate(), but I don't want to write if and elses to check whether I should run Dog() or Cat(). I wonder if it is possible to bark or meow by doing something like:
Animal("dog").communicate()
Where "dog" can be a variable.
Or if possible give the child classes some labelling and be able to instantiate them via this label, or even via the own class name.
The ideia is to not have to write conditions Everytime I define new child child classes.
Thanks in advance!
Factory pattern is your solution.
Aproach to automate conditions for creating classes described here
I can show how metaclasses can be applied:
class MetaAnimal(type):
classes = {}
def __new__(cls, name, bases, dct):
result = super().__new__(cls, name, bases, dct)
cls.classes[name.lower()] = result
return result
#classmethod
def get_animal(cls, name):
return cls.classes.get(name)
class Animal(metaclass=MetaAnimal):
def communicate(self):
pass
class Dog(Animal):
def communicate(self):
self.bark()
def bark(self):
print('Woof')
class Cat(Animal):
def communicate(self):
self.meow()
def meow(self):
print('Meow')
MetaAnimal.get_animal('cat')().communicate()
MetaAnimal.get_animal('dog')().communicate()
I'm implementing enumeration using a base class that defines a variety of methods. The actual enumerations are subclasses of that, with no additional methods or attributes. (Each subclass is populated with its own values using the constructor defined in the base class).
I use a registry (a class attribute that stores all the instances of that class). Ideally, I'd like to avoid defining it in each subclass. Unfortunately, if I define it in the base class, all the subclasses will end up sharing the same registry.
What's a good approach here?
Below is the implementation in case it helps (it's based on #jchl comment in python enumeration class for ORM purposes).
class IterRegistry(type):
def __iter__(cls):
return iter(cls._registry.values())
class EnumType(metaclass = IterRegistry):
_registry = {}
_frozen = False
def __init__(self, token):
if hasattr(self, 'token'):
return
self.token = token
self.id = len(type(self)._registry)
type(self)._registry[token] = self
def __new__(cls, token):
if token in cls._registry:
return cls._registry[token]
else:
if cls._frozen:
raise TypeError('No more instances allowed')
else:
return object.__new__(cls)
#classmethod
def freeze(cls):
cls._frozen = True
def __repr__(self):
return self.token
#classmethod
def instance(cls, token):
return cls._registry[token]
class Enum1(EnumType): pass
Enum1('a')
Enum1('b')
for i in Enum1:
print(i)
# not going to work properly because _registry is shared
class Enum2(EnumType): pass
As you already have a metaclass you might as well use it to put a add a separate _registry attribute to each subclass automatically.
class IterRegistry(type):
def __new__(cls, name, bases, attr):
attr['_registry'] = {} # now every class has it's own _registry
return type.__new__(cls, name, bases, attr)
Marty Alchin has a very nice pattern for this: see his blog entry.
What if you share the same registry, but with sub-registries per class, i.e.
if cls.__name__ not in self._registry:
self._registry[cls.__name__] = {}
self._registry[cls.__name__][token] = cls
You actually don't even need cls.__name__, you should be able to use cls itself as key.