2016-09-02 4 views
0

コアアプリケーションがPython 2.6からPython 2.7に変更され、後のリリースでPython 3に変更される可能性があります。floatを受け入れるためにrange()関数をPython 2.7から拡張/オーバーロードするには?

PythonのRange関数が変更されました(Python 2.7のchangelogからの引用)。

range()関数は、その引数をより一貫して処理します。 はfloat型ではなく__int__()を呼び出します。

さらに処理する結果に基づいて、ユーザーが式/ Pythonコードを追加できるようにしました。

rangeの機能を変更するにはどうすればよいですか?それらの中にはfloat引数を使っていて、Python 2.7で失敗しているものもあります。

コードはユーザーによって書き込まれているため、Pythonコード/式では変更できません。 1000sのファイルがあります。一部のユーザーは、独自のファイルを持つことがあります。

  1. は、それがフロートの引数を取るようパイソンからrange()機能を拡張する方法は、ありますか?

  2. もう1つの方法は、Pythonコードを解析してfloatintに変更することです。それは多くの刺す操作を必要とし、いくつかのrange呼出しがパラメータとして式を有するので、非常に時間がかかる。

私たちのアプリケーションは、C++で構築し、我々はあなたが深刻な移行の問題を持っているC++、PythonのAPIS

+0

floatを受け入れるためにmonkey-patch 'range'を使うことができますが、それはあなたがどのようにユーザコードを呼び出すかによって異なります。詳細を教えていただけますか? –

+0

C++のpythonコードはpstr = PyEval_EvalCode((PyCodeObject *)cs、pdict、pdict)を使用して評価されました。 – Sandip

+0

Python 2.5でもfloat argsを 'range'に渡すと、' DeprecationWarning:integer argument expected、 'float'が得られます。おそらく、この警告を無視してはいけません。 –

答えて

3

使用してPythonの式を評価します。 Python 2.7とPython 3に切り替える場合、基本的には既存の(ユーザーの)コードベースをリファクタリングするのは簡単です。 IMHO、次のオプションがあります。

  1. ユーザーに永続的な(オプションではない)2.6互換インターフェースを提供します。つまり、何も変更する必要はありませんが、ユーザーがPython 2.6のセマンティクスを満たさなければならないため、Python 2.7へのアップグレードの目的を破ります。 判定:推奨しません。

  2. 一時的な(オプションではない)2.6互換インタフェースを、限られた時間提供します。その場合、既存のコードを最終的にリファクタリングする必要があります。 判定:推奨しません。

  3. コードにフラグを入れてください(例えば、# *$$ supertool-pythonversion: 2.7 $$*のようなファイルを実行することなく安全に識別できる魔法のコメント)。コードが実行されるPythonのバージョンと、 Python 2.7でフラグが立てられていません。そうすれば、古いファイルを実行し、新しいファイルをそのまま実行するのに必要な互換性のあるハックを実行することができます。 判定:複雑さは増しますが、移行に役立ちます。 推奨

ただし、C++からPythonを呼び出すのに便利です。したがって、globalslocalsの辞書を使用してスクリプトを実行する環境を制御することができます。PyEval_EvalCodeに転送されます。シナリオ3を実装するには、ファイルから互換性フラグをチェックした後、互換モードを有効にするためにPyEval_EvalCodeを呼び出す前に、floatの引数をgloabls辞書に入れるカスタムのrange関数を置くことができます。

私は、PythonのC APIに堪能ではないですが、Pythonで、これは次のようになります(C APIを介して同じことを行うことが可能です):

range27 = range 

def range26(start=None, stop=None, step=None): 
    if start is not None and not isinstance(start, int): 
     start = int(start) 
    if stop is not None and not isinstance(stop, int): 
     stop = int(stop) 
    if step is not None and not isinstance(step, int): 
     step = int(step) 
    return range27(start, stop, step) 

def execute_user_code(user_file): 
    ... 
    src = read(user_file) 
    global_dict = {} 
    local_dict = {} 
    ... 

    if check_magic_version_comment(src) in (None, '2.6'): 
     global_dict['range'] = range26 
     global_dict['range27'] = range27 
     # the last line is needed because the call 
     # of range27 will be resolved against global_dict 
     # when the user code is executed 

    eval_code(src, global_dict, local_dict) 
    ... 
+0

3つ目の提案では、2つのPythonバージョンのDLLを保持する必要があります。これは難しいようです。コアアプリケーションに新しいPythonが必要な場合、互換性の問題があるかもしれません。 – Sandip

+0

ユーザーが使用する機能に応じて、2つ(またはそれ以上)のPythonバージョンを統合する必要はありません。私は、問題を引き起こす唯一の限られた問題のセットがあると仮定します( 'range'のような)。互換性はPython 2.7上で提供されるべきです。しかし、分析はもちろんあなた次第です;) –

+0

今は範囲だけの問題を参照してください。2.6からの範囲を実行するには2.6のpython dllを提供する必要があります。 – Sandip

2

変更が__int__を呼び出すことではありませんフロートではありません。あなたに影響を与える変更は、浮動小数点引数はPython 2.7で、これ以上受け入れられないです。

Python 2.6.9 (default, Sep 15 2015, 14:14:54) 
[GCC 5.2.1 20150911] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> range(10.0) 
__main__:1: DeprecationWarning: integer argument expected, got float 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

DeprecationWarningを参照してください - すべてのこの時間は、あなたのコードは、警告が発せられたましたが、あなたはそれらを無視することにしました。 Pythonの2.7で:

Python 2.7.12 (default, Jul 1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> range(10.0) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: range() integer end argument expected, got float. 

ソリューションは、新しい関数にrange()をラップすることです:

def py26_range(*args): 
    args = [int(i) if isinstance(i, float) else i for i in args] 
    return range(*args) 

この関数は、Python 2.6の挙動を保持int型に山車を強制します。引数がfloatのコード部分では、rangeのすべての用途をpy26_rangeに置き換える必要があります。

from functools import wraps 
import __builtin__ 

@wraps(range) 
def py26_range(*args): 
    args = [int(i) if isinstance(i, float) else i for i in args] 
    return range(*args)  

__builtin__.range = py26_range 

は、他のモジュールですでもインポート前にこれを実行し、それが以前のように動作するはずです:あなたは十分に必死なら


、あなたは__builtin__rangeのこのバージョンをインストールすることができます。

関連する問題