I am trying to call an instance variable from a "parent" class (subclass) to it's "child" class (subsubclass)
class mainclass():
def __init__(self):
self.mainclassvar1 = "mainclass"
class subclass(mainclass):
def __init__(self):
self.subclassvar1 = "subclass"
def changeval(self):
self.subclassvar1 = "subclassedited"
class subsubclass(subclass):
def __init__(self):
self.subsubclassvar1 = subclass.subclassvar1 #<- naturally this fails
def handler():
main=mainclass()
sub = subclass()
sub.changeval()
subsub = subsubclass()
print(subsub.subsubclassvar1)# <- how do I achieve this? I would expect "subclassedited" but it doesn't
if __name__ == "__main__":
handler()
The above does not work obviously but I am trying to show what I am trying to achieve in my head.
if I change the class subsubclass(subclass) as follows it semi-works:
class subsubclass(subclass):
def __init__(self):
subclass.__init__(self)
self.subsubclassvar1 = self.subclassvar1
however the returned value is the original default value of subclass instead of the expected subclassedited.
I am not sure if I should even be trying to do this but I've got some code where the logic has now come to this point and I want to try see if I can get details from the middle class in to the final child class in their final modified states instead of the defaults and without refactoring a lot of code.
Each __init__ method should be invoking the parent's __init__ method, so that the instance is properly initialized for all the classes in the hierarchy.
class mainclass:
def __init__(self):
super().__init__()
self.mainclassvar1 = "mainclass"
class subclass(mainclass):
def __init__(self):
super().__init__()
self.subclassvar1 = "subclass"
def changeval(self):
self.subclassvar1 = "subclassedited"
class subsubclass(subclass):
def __init__(self):
super().__init__()
# Not sure why you would really need this, but...
self.subsubclassvar1 = self.subclassvar1
There's no reason, though that subsub.subclassvar1 should be related to sub.subclassvar1, though. Calling sub.changeval() has nothing to do with subsub.
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;
I am building a child class inheriting from the xarray Dataset class but I wish to use the xarray open_dataset function to return the instance. The reason for this is that xarray's open_dataset can link to large datasets without loading them to memory as I would have to do when manually initialising a Dataset instance. I do not wish to modify this function and so to get around this I modify the 'new' method for Dataset to return my own class as follows.
def open_dataset(nc_file, data_type):
data_objs = {
"my_class": MyClass,
}
obj = data_objs[data_type]
def __new__(cls, *args, **kwargs):
if cls == xr.Dataset:
return object.__new__(obj)
return object.__new__(cls)
xr.Dataset.__new__ = staticmethod(__new__)
obj = xr.open_dataset(nc_file)
return obj
I can then define my class as follows and am successfully able to obtain MyClass instances from the open_dataset function which have the my_method method accessible and useable.
class MyClass(xr.Dataset)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.my_attribute = 1
print("in the init")
def my_method(self):
print("hello!")
However, the instances do not have my_attribute initialised. I have checked by adding a print statement that 'init' is being executed and I do get a print out. I therefore don't understand why the attributes in this init are not being initialised.
When I create a mixin class that extends the logic of __init__, the regular thing to do is:
class ExtraValuemixin:
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
# some extra initialization
self._extra_value = 1
def retrieve_extra_value(self):
return self._extra_value
However this looks wrong to mypy, as it says:
Too many arguments for "__init__" of "object"
I get it, there's no *args or **kwargs in the object's constructor signature; but this is a mixin, and it relies on its childen's constructors. Ho do I make mypy understand this?
Full example:
class ExtraValuemixin:
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
# some extra initialization
self._extra_value = 1
def retrieve_extra_value(self):
return self._extra_value
class ParentObj:
def __init__(self, value):
self.value = value
class ChildObj(ExtraValuemixin, ParentObj):
pass
obj = ChildObj(value=5)
print(obj.retrieve_extra_value())
super().__init__(...)
Calls the __init__ function of it's parent class.
Defining a class with
class XXX:
(Omitting the hierarchal parent), leaves the parent as the default: our friend and beloved object class.
And we know the source of that class looks like:
class object:
def __init__():
Meaning, that it accepts no arguments. You essentially called this __init__ with two arguments. Hence, your exact error of:
Too many arguments for "__init__" of "object"
I would like to decorate certain instance functions with a decorator from a "parent" instance, is there a way that I can use the instance to decorate the functions.
Here is the thought on what I need to do;
class Foo(object):
def __init__(self):
pass
def set_configuration(self, function):
def config(*args, **kwargs):
# NOTE: this function needs to use instance variables.
print 'foo ' + function()
return config()
class Bar(object):
def __init__(self, parent):
self.parent = parent
#self.parent.set_configuration
def set_config_2(self)
return 'bar'
foo = Foo()
foo.bar = Bar(foo)
foo.bar.set_config_2
EDIT:
Ok guys here is the actual issue, I have a device that i need to interact with. So a device may have several levels to it ie a device a
has multiple interfaces and an interface may have multiple vlans attached. So the idea is that if I want to change a vlan on an interface, instead of building a full command I would like to allow the parent class to handle the building of it's level of the command. So I would like to just call the "change vlan" function and it will send it's part of the command to the next level to be wrapped and sent up the chain till it hits the device level and the full command is sent to the device.
class Device(object):
def __init__(self):
self.interfaces = list()
self.ssh = ssh('blah')
def set_device(self, function):
self.ssh.command('setup commands')
self.ssh.command(wrapped command here)
self.ssh.command('exit commands')
class Interface(object):
def __init__(self, name, parent):
self.name
self.parent
self.vlan = Vlan('name')
def set_interface(self):
return self.name
class Vlan(object):
def __init__(self, name, parent):
self.name = name
self.parent = parent
def set_vlan(self):
return self.name
I hope this makes more sense. if not please let me know.
No, you cannot use decorators here, because at definition time of Bar, parent is not known.
Simply use set_configuration with a argument:
class Foo(object):
def __init__(self):
pass
def set_configuration(self, function):
def config(*args, **kwargs):
# NOTE: this function needs to use instance variables.
print 'foo ' + function()
return config
class Bar(object):
def __init__(self, parent):
self.parent = parent
def set_config_2(self, args)
def inner_function():
return 'bar'
return self.parent.set_configuration(inner_function)(args)
foo = Foo()
foo.bar = Bar(foo)
foo.bar.set_config_2(123)
Python is a dynamic language so many things are possible. I'm making no comment about whether this is a good thing to do or not - and I really can't understand the purpose of your logic.
To make this possible you will need dynamically create the set_config_2 in Bar.__init__ as parent is unknown at the class definition time:
from types import MethodType
class Foo(object):
def __init__(self):
pass
def set_configuration(self, f):
def config(inst, *args, **kwargs):
print('foo', f(inst, *args, **kwargs))
return config
class Bar(object):
def __init__(self, parent):
self.parent = parent
#self.parent.set_configuration
def set_config_2(inst):
return 'bar'
self.set_config_2 = MethodType(set_config_2, self)
foo = Foo()
foo.bar = Bar(foo)
foo.bar.set_config_2()
Output:
foo bar
This is desperately ugly and there must be a better way of doing what you are attempting. Perhaps you can ask a different question explaining what you are trying to achieve.
Your decorator does not have to use instance methods, since that's the wrapping function config who needs them. Therefore, the decorator does not have to be a method. For example:
def set_configuration(func):
#functools.wraps(func) # copy function's metadata
def wrapper(self, *args, **kwargs):
# do whatever you want to fetch the config data
return 'foo' + func(self, *args, **kwargs)
return wrapper
That said, there likely is a more straightforward and explicit way, depending on what exactly you want.
I'm pretty sure you can do this without making the decorator an instance. Here are a couple ideas.
Invert the hierarchy
It seems to me like the hierarchy you have is backwards. My understanding:
Device is only providing the ssh instance
The common method you want to call is something the VLAN defines
The setup and exit commands are constants
By making the hierarchy go the other way, you can define the "change VLAN" method to access stuff from the lower levels that it needs.
class Device(object):
def __init__(self):
self.ssh = ssh('blah')
class Interface(object):
def __init__(self, name, device):
self.name
self.device = device
class Vlan(object):
def __init__(self, name, change_command, interface):
self.name = name
# How you actually store this command is completely up to you.
# You might want to shove it in an abstract method
# and subclass Vlan, but the point is make it part of the
# Vlan somehow.
self.change_command = change_command
self.interface = interface
def change_vlan(self):
ssh = self.interface.device.ssh
ssh.command('setup commands')
ssh.command(self.change_command)
ssh.command('exit commands')
device1 = Device()
device2 = Device()
interface1 = Interface('i1', device1)
interface2 = Interface('i2', device1)
interface3 = Interface('i3', device2)
vlans = [
Vlan('v1', 'change 1', interface1)
Vlan('v2', 'change 2', interface1)
Vlan('v3', 'change 3', interface2)
Vlan('v4', 'change 4', interface3)
]
This might not show exactly what you want to do, but hopefully it demonstrates how you can set this up with the hierarchy going the other way.
Make The decorator accept a Device
Alternatively, if you still think decorating is a better option, you can make the decorate accept the instances you need.
def ssh_command(device, function):
def execute_ssh_command(*args, **kwargs):
device.ssh.command('setup commands')
device.ssh.command(wrapped command here)
device.ssh.command('exit commands')
# Note: no parentheses. You're returning the function itself
return execute_ssh_command
class Interface(object):
def __init__(self, name, parent):
self.name
self.parent
self.vlan = Vlan('name')
#ssh_command
def set_interface(self):
return self.name
Note you'll need to make a separate subclass per whatever thing uses the decorator.