2016-06-29 18 views
10

目的:呼び出し元から要求された単位(または任意の変更)の関数から値を返します。関数を適切な単位で返すPythonの方法

背景:

私はラズベリーパイ3でPython 2.7を実行している、とロータリーエンコーダが変わった距離を取得する機能distance()を使用しています。この距離は、関数が呼び出される場所によって異なる単位で必要です。それでは、これはどういう意味になるのでしょうか?

最初の試行:

私の最初の試みは、関数でメートルのユニットを使用し、中に戻るには、右のユニットを選択するために、長いelifツリーを持っていることでした

def distance(units='m'): 
    my_distance = read_encoder() 

    if units == 'm': 
     return my_distance * 1.000 
    elif units == 'km': 
     return my_distance/1000. 
    elif units == 'cm': 
     return my_distance * 10.00 
    elif units == 'yd': 
     return my_distance * 1.094 
    else: 
     return -1 

このアプローチの良い点は、利用できないユニットを認識する方法があることです。

第二の試み:

私の第二の試みは様々なmultipliersを格納するための辞書を作成することでした。

def distance(units='m'): 
    multiplier = { 
     'm': 1.000, 
     'km': 0.001, 
     'cm': 10.00 
     'yd': 1.094 
    } 

    try: 
     return read_encoder() * mulitplier[units] 
    except KeyError: 
     return -1 

ここでは、認識できない単位はKeyErrorでキャッチされます。

関連性:

私はPintのような既存のライブラリを知っているが、この計画問題への解決策を探しています。 Pythonで関数があり、再利用可能な方法で出力をわずかに変更する必要があるとき。私はspeed()のような 'm/s'を基本単位とし、同様のunits引数を必要とする他の関数を持っています。私の経験から、よく構造化されたプログラムはすべてのreturn文の前にelifの段落の段落を伴わない。この場合、ユニットをどのように計算するかを変更したいのであれば、コードを使って慎重にgrepする必要があり、すべてのインスタンスでユニットがどのように計算されるかを変更する必要があります。適切な解決策は、計算を一度変更するだけで済みます。

これは広すぎる可能性がありますが、それは私が走り続けているパターンです。

+1

、私はそれを使用するために多くのことを意図している場合、私の内部のC#devのは、拡張メソッドを作ると言うでしょうが個人的に私が '、第二の試みとなるだろう.convertTo( 'ms') 'などです。その後、 'encoderReading.convertTo( 'm')'を実行すると、構文が本当にうまく見えると思います。 –

+0

あなたはあなたのすべての値が10の累乗であることを知っています。また、整数は文字列よりも速いことも知っていますので、mapとlambdaを使って乗数の静的な値を動的に設定した値にマッピングすることで、 – dmitryro

+0

残念ながら、私の実際の実装には非SI単位(つまりヤード、マイル)もあります。誰もが選択単位でデータを表示することを望んでいます。私はそれを反映するために私の質問を更新します。 –

答えて

6

方法について、デコレータを使用して:

def read_encoder(): 
    return 10 

multiplier = { 
    'm': 1.000, 
    'km': 0.001, 
    'cm': 10.00, 
    'yd': 1.094, 
} 

def multiply(fn): 
    def deco(units): 
     return multiplier.get(units, -1) * fn(units) 
    return deco 

@multiply 
def distance(units='m'): 
    my_distance = read_encoder() 
    return my_distance 

print distance("m") 
print distance("yd") 
print distance("feet") 

出力:

multiplier = { 
    'm': 1.000, 
    'km': 0.001, 
    'cm': 10.00, 
    'yd': 1.094, 
} 

def multiply(fn): 
    def deco(units, *args, **kwds): 
     return multiplier.get(units, -1) * fn(*args, **kwds) 
    return deco 


@multiply 
def read_encoder(var): 
    #I've added a variable to the function just to show that 
    #it can be passed through from the decorator 
    return 10 * var 

print read_encoder("m", 1) 
print read_encoder("yd", 2) 
print read_encoder("feet", 3) 

10.0 
10.94 
-10 

か、いずれかのユニットレス機能を一周より汎用ラッパーとして出力:

10.0 
21.88 
-30 

KeyError対-1を引き上げるビットは味の問題です。個人的には、見つけられなければ* 1を返します(受信者が気にしなかった場合)。または、KeyErrorを投げる。 -1は明らかに有用ではありません。

最後の反復、ユニットのパラメータはオプション作る:

def multiply(fn): 
    def deco(*args, **kwds): 
     #pick up the unit, if given 
     #but don't pass it on to read_encoder 
     units = kwds.pop("units", "m") 

     return multiplier.get(units, -1) * fn(*args, **kwds) 
    return deco 


@multiply 
def read_encoder(var): 
    return 10 * var 

print read_encoder(1, units="yd") 
print read_encoder(2) 
print read_encoder(3, units="feet") 


10.94 
20.0 
-30 
5

辞書ルックアップは良いですが、エラーを通知するためにセンチネル値を返しません。適切な例外を発生させるだけです。あなたのルックアップのKeyErrorを伝播させるのと同じくらい単純な(不透明ですが)かもしれません。ご例えばそれができる

class UnknownUnitError(ValueError): 
    pass 

def distance(unit='m'): 
    multiplier = { 
     'm': 1.000, 
     'km': 0.001, 
     'cm': 10.00 
    } 

    try: 
     unit = multiplier[unit] 
    except KeyError: 
     # Include the problematic unit in the exception 
     raise UnknownUnitError(unit) 

    return read_encoder() * unit 
+1

簡単で簡単な回答ではうまくいきますが、一般的にこのような空のクラスを定義するときにpassを使うべきではなく、代わりにクラスを説明するdoc文字列を使用してください。 – Keozon

4

:よりよい解決策は、しかし、カスタム例外を発生することである

class DistanceUnits(): 
    """ 
    Enum class for indicating measurement units and conversions between them. 
    Holds all related logic. 
    """ 
    M = 'm' 
    KM = 'km' 
    CM = 'cm' 


def distance(units=DistanceUnits.M): 
    multiplier = { 
     DistanceUnits.M: 1.000, 
     DistanceUnits.KM: 0.001, 
     DistanceUnits.CM: 10.00 
    } 
    return read_encoder() * mulitplier[units] if units in multiplier else -1 

しかし、distance機能のmultipliers外を移動することが合理的であるとそれを作ることができますDistanceUnitsの部分。

UPD:のさまざまな方法がたくさんあります「どのようには..」とそれらのすべてが必要であるあなたに依存するが、一つのメイン原則DRYがあります。 elifでも十分です(辞書インスタンスを作成すると、各関数呼び出しでいくつかのRAMが消費されます)。自分自身を繰り返すことを忘れないでください。

関連する問題