We have the following class structure:
class NamePrinter():
def phone():
print(self.name)
def email():
print(self.name)
class PhoneCaller(NamePrinter):
def __init__(self, name, number, mail):
self.name = name
self.number = number
self.mail = mail
def phone(self):
# here, NamePrinter.phone() should be executed
compose_number(self.number)
def email(self):
# here, NamePrinter.email() should be executed
compose_mail(self.mail)
I want NamePrinter.phone()
to be executed when PhoneCaller.phone()
is called, without having to mention super.phone()
in PhoneCaller.
The idea is that the only modification to be applied to PhoneCaller in order for it to execute NamePrinter's behaviour when PhoneCaller.phone is executed, is that PhoneCaller inherits from parent, and nothing more. In particular, no need to modify any individual PhoneCaller method.
To put it simpler:
Is this possible?
Yes, it is possible at least with metaclass:
def new_attr(attr_name, attr):
name_printer_attr = getattr(NamePrinter, attr_name)
def _new_attr(self, *args, **kwargs):
name_printer_attr(self)
return attr(self, *args, **kwargs)
return _new_attr
class Meta(type):
def __new__(cls, name, bases, attrs):
if name == 'NamePrinter':
cls.attrs = attrs
else:
for attr_name, attr in attrs.items():
if callable(attr) and attr_name in cls.attrs:
attrs[attr_name] = new_attr(attr_name, attr)
return type.__new__(cls, name, bases, attrs)
class NamePrinter(metaclass=Meta):
def phone(self):
print('NamePrinter phone')
class PhoneCaller1:
def phone(self):
print('PhoneCaller1 phone')
class PhoneCaller2(NamePrinter):
def phone(self):
print('PhoneCaller2 phone')
p1 = PhoneCaller1()
p1.phone() # will print only "PhoneCaller1 phone"
p2 = PhoneCaller2()
p2.phone() # will print "NamePrinter phone" and "PhoneCaller2 phone" on next line
And there is another solution with decorator. It saves you from misusing inheritance and is more clear and flexible (IMHO):
def new_attr(attr_name, attr, from_cls):
from_cls_attr = getattr(from_cls, attr_name)
def _new_attr(self, *args, **kwargs):
from_cls_attr(self)
return attr(self, *args, **kwargs)
return _new_attr
def use_methods(from_cls):
dir_from_cls = dir(from_cls)
def modify(cls):
for attr_name in dir(cls):
if not attr_name.startswith('__') and attr_name in dir_from_cls:
attr = getattr(cls, attr_name)
if callable(attr):
setattr(cls, attr_name, new_attr(attr_name, attr, from_cls))
return cls
return modify
class NamePrinter:
def phone(self):
print('NamePrinter phone')
class PhoneCaller1:
def phone(self):
print('PhoneCaller1 phone')
@use_methods(NamePrinter)
class PhoneCaller2:
def phone(self):
print('PhoneCaller2 phone')
p1 = PhoneCaller1()
p1.phone()
p2 = PhoneCaller2()
p2.phone()
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.