2017-01-24 5 views
1

私はpysparkパイプラインのコンストラクタをラップしようとしています。 コンストラクタ、および新しくラップされたコンストラクタのサルパッチ。しかし、私はパイプラインのやり方と何か関係があると思われるエラーに遭遇しています。ラップpysparkパイプライン.__ init__とデコレータ

import PythonSparkCombinatorLibrary 
from pyspark.ml import Pipeline 
from pyspark.ml.classification import LogisticRegression 
from pyspark.ml.feature import HashingTF, Tokenizer 

PythonSparkCombinatorLibrary.TransformWrapper.monkeyPatchPipeline() 
tokenizer = Tokenizer(inputCol="text", outputCol="words") 
hashingTF = HashingTF(inputCol=tokenizer.getOutputCol(),outputCol="features") 
lr = LogisticRegression(maxIter=10, regParam=0.001) 

pipeline = Pipeline(stages=[tokenizer, hashingTF, lr]) 

私はこのエラーを取得する:

def monkeyPatchPipeline(): 
     oldInit = Pipeline.__init__ 

     def newInit(self, **keywordArgs): 
     oldInit(self, stages=keywordArgs["stages"]) 

     Pipeline.__init__ = newInit 

私は簡単なプログラムを実行すると、しかし:のinitはここ

が実際にモンキーパッチを行うコードがあるデコレータを使用しています

Traceback (most recent call last): 
    File "C:\<my path>\PythonApplication1\main.py", line 26, in <module> 
    pipeline = Pipeline(stages=[tokenizer, hashingTF, lr]) 
    File "C:<my path>PythonApplication1 \PythonSparkCombinatorLibrary.py", line 36, in newInit 
oldInit(self, stages=keywordArgs["stages"]) 
    File "C:\<pyspark_path>\pyspark\__init__.py", line 98, in wrapper 
    return func(*args, **kwargs) 
File "C:\<pyspark_path>\pyspark\ml\pipeline.py", line 63, in __init__ 
    kwargs = self.__init__._input_kwargs 
AttributeError: 'function' object has no attribute '_input_kwargs' 

pyspark interfaを見ると私はそのパイプラインを見る。 のinitは次のようになります。

@keyword_only 
def __init__(self, stages=None): 
    """ 
    __init__(self, stages=None) 
    """ 
    if stages is None: 
     stages = [] 
    super(Pipeline, self).__init__() 
    kwargs = self.__init__._input_kwargs 
    self.setParams(**kwargs) 

そして@keyword_onlyデコレータに注意して、私もそのコードを検査:

def keyword_only(func): 
    """ 
    A decorator that forces keyword arguments in the wrapped method 
    and saves actual input keyword arguments in `_input_kwargs`. 
    """ 
    @wraps(func) 
    def wrapper(*args, **kwargs): 
     if len(args) > 1: 
      raise TypeError("Method %s forces keyword arguments." % func.__name__) 
     wrapper._input_kwargs = kwargs 
     return func(*args, **kwargs) 
    return wrapper 

私はこのコードは最初の場所でどのように機能するかについての両方全く混乱していますまた、自分のラッパーに問題を引き起こすように見える理由もあります。私はラッパーが_input_kwargsフィールドを自分自身に追加しているのを見ますが、Pipeline .__ init__はそのフィールドをself .__ init __._ input_kwargsでどのように読むのでしょうか?なぜ私はパイプライン.__ init__をもう一度ラップすると同じことは起こりませんか?

答えて

0

デコレータ101デコレータは、関数を第1引数(通常は通常)として受け取り、関数を返す高次関数です。だから、Pipeline.__init__が実際に__init__func引数を定義してキャプチャしfunctools.wrappedwrapperある

def decorated_(x): 
    ... 

decorated = decorator(decorated_) 

@注釈はとてもとして

@decorator 
def decorated(x): 
    ... 

は、例えば書き換えることができ、以下、簡単な関数呼び出しのためだけ糖衣構文でありますkeyword_only)を閉鎖の一部として使用します。呼び出されると、受信したkwargsをそのままfunction attributeとして使用します。外部関数は、したがって、_input_kwargsが付加された誤りを持っていません__init__(飾る)あなたは、さらにラップすると

def f(**kwargs): 
    f._input_kwargs = kwargs # f is in the current scope 

hasattr(f, "_input_kwargs") 
False 
f(foo=1, bar="x") 

hasattr(f, "_input_kwargs") 
True 

:基本的には何がのように簡略化することができ、ここで起こります。あなたはそれを動作させるにしたい場合は、元の__init__で使用されるように、あなたは同じデコレータで、たとえば、独自のバージョンに、同じプロセスを適用しています

@keyword_only 
def newInit(self, **keywordArgs): 
    oldInit(self, stages=keywordArgs["stages"]) 

が、私はコメントで言及言っています、あなたサブクラス化を考慮する必要があります。

関連する問題