Python 向以前定义的函数添加功能的优雅方式 如何将两个功能结合在一起
我有一个类控制一些硬件:Python 向以前定义的函数添加功能的优雅方式 如何将两个功能结合在一起,python,decorator,Python,Decorator,我有一个类控制一些硬件: class Heater() def set_power(self,dutycycle, period) ... def turn_on(self) ... def turn_off(self) 以及一个连接到数据库并处理实验的所有数据记录功能的类: class DataLogger() def __init__(self) # Record measurements and controls
class Heater()
def set_power(self,dutycycle, period)
...
def turn_on(self)
...
def turn_off(self)
以及一个连接到数据库并处理实验的所有数据记录功能的类:
class DataLogger()
def __init__(self)
# Record measurements and controls in a database
def start(self,t)
# Starts a new thread to acquire and record measuements every t seconds
现在,在我的程序recipe.py中,我想做如下操作:
log = DataLogger()
@DataLogger_decorator
H1 = Heater()
log.start(60)
H1.set_power(10,100)
H1.turn_on()
sleep(10)
H1.turn_off()
etc
其中H1上的所有操作都由数据记录器记录。我可以改变任何涉及的类,只是寻找一种优雅的方式来做到这一点。理想情况下,硬件功能与数据库和数据记录器功能保持分离。理想情况下,数据记录器可用于其他控件和测量。对于这种情况,我更喜欢将数据记录器用作基类或混入其他类,而不是尝试执行某种装饰器魔术(这对我来说并不是一种使用装饰器的pythonic方式) e、 g: 这样你就可以做到:
h1 = Heater()
h1.startlog(5)
h1.do_other_stuff()
将其用作现有类的mixin的示例:
class DataLoggerMixin(object):
def __init__(self):
# do your init things
super(DataLogger, this).__init__() # this will trigger the next __init__ call in the inheritance chain (i.e. whatever you mix it with)
class Heater(object):
""" Here's a heater you have lying around that doesn't do data logging. No need to change it."""
# add a new child class with 2 lines, that includes the DataLoggerMixin as the first parent class, and you will have a new class with all the DataLogging functionality and the Heater functionality.
class LoggingHeater(DataLoggerMixin, Heater):
""" Now its a data logging heater """
pass # no further code should be necessary if you list DataLoggerMixin first in the base classes.
>>> logging_heater = LoggingHeater()
>>> logging_heater.start_log(5)
>>> logging_heater.do_heater_stuff()
在python中成功使用mixin的关键是理解方法解析顺序(methodresolutionorder,MRO),特别是对于super,在多重继承情况下是如何工作的。请参见协作多重继承
____________________________________________________________________
替代方法:使用包装类
如果Mixin方法不适用于您的方案,另一个选项是使用数据记录器作为要记录的对象的包装类。基本上,Data Logger会接受一个对象在其构造函数中进行登录,如下所示:
class DataLogger(object)
def __init__(self, object_to_log)
self.object = object_to_log # now you have access to self.object in all your methods.
# Record measurements and controls in a database
def start(self,t)
# Starts a new thread to aqcuire and reccord measuements every t secconds
我不确定完成了什么类型的日志记录或监视,以及您是否需要访问正在记录的对象,或者它是否是独立的。如果前者(可能是加热器、阀门等)都实现了数据记录器关心的相同功能,那么无论它们是什么类,您都可以为它们进行记录。(这是动态语言(如Python)的一个方便的核心功能,称为“Duck typing”,您可以在不同的类型上操作,只要这些类型实现了您关心的函数或属性。“如果它像鸭子一样嘎嘎……”
使用包装类方法,您的代码可能更像这样:
h1 = Heater()
log = DataLogger(h1)
log.start(60)
h1.set_power(10,100)
h1.turn_on()
sleep(10)
h1.turn_off()
希望这有帮助 对于这个场景,我更喜欢使用数据记录器作为基类或其他类的Mixin,而不是尝试使用某种装饰器魔术(这对我来说并不是一种使用装饰器的python方式) e、 g: 这样你就可以做到:
h1 = Heater()
h1.startlog(5)
h1.do_other_stuff()
将其用作现有类的mixin的示例:
class DataLoggerMixin(object):
def __init__(self):
# do your init things
super(DataLogger, this).__init__() # this will trigger the next __init__ call in the inheritance chain (i.e. whatever you mix it with)
class Heater(object):
""" Here's a heater you have lying around that doesn't do data logging. No need to change it."""
# add a new child class with 2 lines, that includes the DataLoggerMixin as the first parent class, and you will have a new class with all the DataLogging functionality and the Heater functionality.
class LoggingHeater(DataLoggerMixin, Heater):
""" Now its a data logging heater """
pass # no further code should be necessary if you list DataLoggerMixin first in the base classes.
>>> logging_heater = LoggingHeater()
>>> logging_heater.start_log(5)
>>> logging_heater.do_heater_stuff()
在python中成功使用mixin的关键是理解方法解析顺序(methodresolutionorder,MRO),特别是对于super,在多重继承情况下是如何工作的。请参见协作多重继承
____________________________________________________________________
替代方法:使用包装类
如果Mixin方法不适用于您的方案,另一个选项是使用数据记录器作为要记录的对象的包装类。基本上,Data Logger会接受一个对象在其构造函数中进行登录,如下所示:
class DataLogger(object)
def __init__(self, object_to_log)
self.object = object_to_log # now you have access to self.object in all your methods.
# Record measurements and controls in a database
def start(self,t)
# Starts a new thread to aqcuire and reccord measuements every t secconds
我不确定完成了什么类型的日志记录或监视,以及您是否需要访问正在记录的对象,或者它是否是独立的。如果前者(可能是加热器、阀门等)都实现了数据记录器关心的相同功能,那么无论它们是什么类,您都可以为它们进行记录。(这是动态语言(如Python)的一个方便的核心功能,称为“Duck typing”,您可以在不同的类型上操作,只要这些类型实现了您关心的函数或属性。“如果它像鸭子一样嘎嘎……”
使用包装类方法,您的代码可能更像这样:
h1 = Heater()
log = DataLogger(h1)
log.start(60)
h1.set_power(10,100)
h1.turn_on()
sleep(10)
h1.turn_off()
希望这有帮助 您可以装饰加热器,并向装饰者提供记录器作为参数:
# define the primitive logger
class Logger(object):
def log(self, func, args, kwargs):
print "Logging %s %r %r" % (func, args, kwargs)
# define the decorator
# since it accepts an argument, it's essentially a decorator generator
# which is supposed to return the actual decorator
# which in turn adds a logger call to each method of the class
def with_logger(logger):
def method_wrapper(cls):
def wrap(name, fun):
def _(self, *a, **kw):
logger.log(name, a, kw)
return fun(self, *a, **kw)
return _
for k in dir(cls):
v = getattr(cls, k)
if not k.startswith('__') and callable(v):
setattr(cls, k, wrap(k, v))
return cls
return method_wrapper
# create a logger...
my_logger = Logger()
# ...and pass it to the decorator
@with_logger(my_logger)
class Heater(object):
def set_power(self,dutycycle, period):
pass
def turn_on(self):
pass
def turn_off(self):
pass
# let's test!
h = Heater()
h.set_power(100, 200)
h.turn_on()
h.turn_off()
您可以装饰加热器,并将记录器作为参数提供给装饰者:
# define the primitive logger
class Logger(object):
def log(self, func, args, kwargs):
print "Logging %s %r %r" % (func, args, kwargs)
# define the decorator
# since it accepts an argument, it's essentially a decorator generator
# which is supposed to return the actual decorator
# which in turn adds a logger call to each method of the class
def with_logger(logger):
def method_wrapper(cls):
def wrap(name, fun):
def _(self, *a, **kw):
logger.log(name, a, kw)
return fun(self, *a, **kw)
return _
for k in dir(cls):
v = getattr(cls, k)
if not k.startswith('__') and callable(v):
setattr(cls, k, wrap(k, v))
return cls
return method_wrapper
# create a logger...
my_logger = Logger()
# ...and pass it to the decorator
@with_logger(my_logger)
class Heater(object):
def set_power(self,dutycycle, period):
pass
def turn_on(self):
pass
def turn_off(self):
pass
# let's test!
h = Heater()
h.set_power(100, 200)
h.turn_on()
h.turn_off()
你知道吗,
@decorator
只是一种简单的语法糖,它的作用是什么?是的,我想,我有一种印象,就是我仍然怀念它能带来的深层技巧。也许decorator一点用都没有。你知道吗,@decorator
只是一种简单的语法糖类,它的作用是什么吗?是的,我想,一半的时候,我觉得我仍然怀念它能带来的深刻的技巧。也许装潢师的事根本帮不上忙。谢谢你的帮助。此外,我们还有一些类:Valve()、Temperature()、Pressure()以及可能更多的类。我想如果我在添加这些时不必更换记录器和加热器,那会更好。看来“Mixin”是我进一步学习的关键词,谢谢!Mixin只是类的另一个词,可以用作补充基类,向其他类添加附加功能。根据定义,mixin包含多重继承方案。使用类似我上面介绍的Mixin,您可以通过为每个基类创建两行子类来添加功能,而无需更改基类(阀、温度、压力等),例如。,LoggingHeater在继承列表中首先包括数据记录器mixin。如果我在上面描述的mixin方法或他的答案中描述的decorator方法不适合,另一个选项是将数据记录器用作要记录的对象的包装类。这可能是三种方法中最容易理解的。我在上面添加了代码来演示。我注意到你是新来的,所以,为了好的因果报应,确保早晚标记一个被接受的答案,这样你就一定能得到你问题的未来答案。谢谢你的帮助。此外,我们还有一些类:Valve()、Temperature()、Pressure()以及可能更多的类。我想如果我在添加这些时不必更换记录器和加热器,那会更好。看来“Mixin”是我进一步学习的关键词,谢谢!Mixin只是类的另一个词,可以用作补充基类,向其他类添加附加功能。根据定义,mixin包含一个m