1

2つの機能を組み合わせる方法

いくつかのハードウェアを制御するクラスがあります。

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でのすべてのアクションがデータロガーによって記録される場所。関係するクラスを変更することができます。これを行うためのエレガントな方法を探しているだけです。理想的には、ハードウェア機能はデータベースおよびDataLogger機能から分離されたままです。そして理想的には、DataLoggerは他の制御や測定に再利用できます。

4

2 に答える 2

2

Heater を装飾し、Logger を引数としてデコレーターに提供できます。

# 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()
于 2012-06-07T23:37:45.057 に答える
2

このシナリオでは、ある種のデコレータ マジックを実行しようとするよりも、他のクラスの BaseClass または Mixin として DataLogger を使用することを好みます (デコレータを使用する Pythonic な方法としては、実際にはクリックしません)。

例えば:

class DataLogger(object):
  def __init__(self):
      # do init stuff

  def startlog(self, t):
      # start the log 


class Heater(DataLogger):
   def __init__(self):
      # do some stuff before initing your dataLogger
      super(Heater, self).__init__() # init the DataLogger
   #other functions

そうすれば、次のことができます:

h1 = Heater()
h1.startlog(5)
h1.do_other_stuff()

既存のクラスのミックスインとして使用する例:

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 でミックスインをうまく使用するための鍵は、メソッド解決順序 (MRO) が、特にスーパーの場合、多重継承の状況でどのように機能するかを理解することです。協調多重継承についてはこちらをご覧ください。

____________________________________________________________________

別の方法: ラッパー クラスを使用する

スキームで Mixin の方法論が機能しない場合は、ログに記録するオブジェクトのラッパー クラスとして DataLogger を使用するという別のオプションがあります。基本的に、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

どのタイプのロギングまたはモニタリングが行われているか、ロギングしているオブジェクトへのアクセスが必要かどうか、またはそれが独立しているかどうかはわかりません。前者、おそらくヒーター、バルブなどはすべて、DataLogger が気にするのと同じ機能を実装しているため、クラスに関係なくログを記録できます。(これは「ダック タイピング」と呼ばれる Python のような動的言語の便利なコア機能であり、型が関心のある関数または属性を実装している限り、さまざまな型を操作できます。 ")

ラッパー クラスの方法論を使用すると、コードは次のようになります。

h1 = Heater() 
log = DataLogger(h1)
log.start(60)
h1.set_power(10,100)
h1.turn_on()
sleep(10)
h1.turn_off()

お役に立てれば!

于 2012-06-07T22:37:02.740 に答える