私がpythonがdel
キーワードを必要とする理由を私は本当に考えることができません(そしてほとんどの言語は類似のキーワードを持っていないようです)。たとえば、変数を削除するのではなく、単にNone
を割り当てることができます。そして辞書から削除するとき、del
メソッドを追加することができます。
del
をpythonで保持する理由はありますか?それともPythonのガーベッジコレクション前の時代の名残ですか?
まず、ローカル変数以外にも他のものを削除できます。
del list_item[4]
del dictionary["alpha"]
どちらも明らかに役に立つはずです。次に、ローカル変数にdel
を使用すると、意図が明確になります。比較しなさい:
del foo
に
foo = None
del foo
の場合、その目的はスコープから変数を削除することです。 foo = None
がそれを行っていることは明らかではありません。誰かがfoo = None
を割り当てたばかりの場合、私はそれがデッドコードであると思うかもしれません。しかし、私は即座にdel foo
をコード化している誰かがやろうとしていたことを知っています。
del
が行うことのこの部分があります( Python言語リファレンス から):
名前を削除すると、その名前のバインドがローカルまたはグローバル名前空間から削除されます。
名前にNone
を割り当てても、名前空間から名前のバインディングは削除されません。
(名前の束縛を取り除くことが実際に 役に立つ であるかどうかに関する議論があるかもしれませんが、それはまた別の質問です。)
del
が便利なことがわかった場所の1つは、forループ内の余分な変数を整理することです。
for x in some_list:
do(x)
del x
これで、forループの外側でxを使用すると、xが未定義になることを確認できます。
あなたが例外を検査するためにsys.exc_info()
を使っているとき、あなたがdel
を使うべきであるという具体的な例があります(他にもあるかもしれませんが、私はこれを手に入れています)。この関数は、Tuple、発生した例外の種類、メッセージ、およびトレースバックを返します。
最初の2つの値は通常、エラーを診断して対処するのに十分ですが、3番目の値には、例外が発生した場所と例外が捕捉された場所の間の呼び出しスタック全体が含まれます。特に、あなたが好きなことをしたら
try:
do_evil()
except:
exc_type, exc_value, tb = sys.exc_info()
if something(exc_value):
raise
トレースバックtb
は呼び出しスタックのローカルに入り、ガベージコレクションできない循環参照を作成します。したがって、次のことを行うことが重要です。
try:
do_evil()
except:
exc_type, exc_value, tb = sys.exc_info()
del tb
if something(exc_value):
raise
循環参照を解除します。メタクラスのマジックのようにsys.exc_info()
を呼び出したいと思う多くの場合、トレースバック は - 便利ですので、例外ハンドラを終了する前に必ずそれをクリーンアップする必要があります。トレースバックが不要な場合は、ただちに削除するか、単に次のようにします。
exc_type, exc_value = sys.exc_info()[:2]
それをすべて一緒に避けるために。
ちょっと考えただけです。
Djangoのようなフレームワークでhttpアプリケーションをデバッグするとき、特にそれが非常に長いリストであるときに、以前に使われた無用でめちゃくちゃになった変数でいっぱいの呼び出しスタックは開発者にとって非常に痛いかもしれません。そのため、この時点では名前空間の制御が便利です。
del
で変数名を削除することはめったに使用されないものですが、それはキーワードなしでは簡単に達成できないものです。あなたがa=1
を書くことによって変数名を作成することができるならば、それを削除することによって理論的にこれを元に戻すことができるのはいいことです。
削除された変数にアクセスしようとするとNameErrorが発生するため、デバッグが容易になる場合があります。
Pythonでは、次のように書くことができます。
class A(object):
def set_a(self, a):
self.a=a
a=A()
a.set_a(3)
if hasattr(a, "a"):
print("Hallo")
クラスインスタンスに動的に属性を追加することを選択した場合は、次のように記述して元に戻すことができます。
del a.a
"del"を明示的に使用することは、変数をNoneに割り当てるよりも良い方法です。存在しない変数を削除しようとするとランタイムエラーが発生しますが、存在しない変数をNoneに設定しようとすると、Pythonは新しい変数を黙ってNoneに設定します。それがあった場所で削除したかった。それでdelはあなたがより早くあなたの間違いをつかむのを助けるでしょう
上記の答えにいくつかのポイントを追加するには:del x
x
の定義はr -> o
(オブジェクトr
を指す参照o
)を示しますが、del x
はr
ではなくo
を変更します。これはx
に関連付けられたオブジェクトではなく、オブジェクトへの参照(ポインタ)に対する操作です。ここではr
とo
を区別することが重要です。
locals()
から取り除きます。x
がそこに属している場合、それをglobals()
から削除します。x
が指す場所ではなく、x
が属する場所に影響します。メモリの物理的な変化はこれだけです。たとえばx
が辞書またはリストにある場合、それは(参照として)そこから削除されます(必ずしもオブジェクトプールからは削除されません)。この例では、辞書が属する辞書はlocals()
と重なるスタックフレーム(globals()
)です。del
は__init__.py
ファイルでよく見られます。 __init__.py
ファイルで定義されているグローバル変数は自動的に「エクスポート」されます(これはfrom module import *
に含まれます)。これを避ける1つの方法は__all__
を定義することですが、これは面倒になることがあり、誰もがそれを使用するわけではありません。
たとえば、__init__.py
のコードがあるとします。
import sys
if sys.version_info < (3,):
print("Python 2 not supported")
それからあなたのモジュールはsys
名をエクスポートします。代わりに書くべきです
import sys
if sys.version_info < (3,):
print("Python 2 not supported")
del sys
numpy.loadを使用した後にファイルを強制的に閉じる:
ニッチな使い方かもしれませんが、 numpy.load
を使ってファイルを読むときに便利です。ときどきファイルを更新し、同じ名前のファイルをディレクトリにコピーする必要があります。
私はdel
を使用してファイルを解放し、新しいファイルにコピーできるようにしました。
注意コマンドラインでプロットを試していて、tabをあまり押さないでください。with
コンテキストマネージャは避けたいと思います。
これ 質問を参照してください。
例として del
を使うことができるものの/ /のように、私はこのような状況でそれが役に立つと思います:
def f(a, b, c=3):
return '{} {} {}'.format(a, b, c)
def g(**kwargs):
if 'c' in kwargs and kwargs['c'] is None:
del kwargs['c']
return f(**kwargs)
# g(a=1, b=2, c=None) === '1 2 3'
# g(a=1, b=2) === '1 2 3'
# g(a=1, b=2, c=4) === '1 2 4'
これら2つの関数は異なるパッケージ/モジュールに存在することができ、プログラマはc
のデフォルト値引数f
が実際に持っているものを知る必要はありません。したがって、kwargsをdelと組み合わせて使用すると、Noneに設定することで「cのデフォルト値が必要です」と言うことができます(この場合も、そのままにします)。
次のようにしても同じことができます。
def g(a, b, c=None):
kwargs = {'a': a,
'b': b}
if c is not None:
kwargs['c'] = c
return f(**kwargs)
しかし、私は前の例をもっとDRYそしてエレガントに見つけました。
Pythonでdelはいつ役に立ちますか?
スライス構文x[i:i+1]=[]
の代わりにこれを使用して、配列の単一要素を削除できます。これは、例えばあなたがos.walk
の中にいて、そのディレクトリの中の要素を削除したい場合に役立ちます。ただし、[].remove(index)
メソッドを作成することもできるので(.remove
メソッドは実際には値の最初のインスタンスを検索して削除します)、この方法でキーワードを使用することはあまり役に立ちません。
Delが独自の構文を持っている理由の1つは、それがバインディングまたは変数に対して機能し、それが参照する値に対しては機能しないことを考えると、場合によってはそれを関数で置き換えるのは難しいかもしれないからです。したがって、関数バージョンのdelを作成する場合は、コンテキストを渡す必要があります。del fooはglobals()。remove( 'foo')またはlocals()になる必要があります。そして読みにくい。それでも、私はdelを取り除くことがその一見めったに使用されないことを考えると良いだろうと言います。しかし、言語の機能や欠陥を取り除くのは面倒です。多分python 4はそれを削除するでしょう:)
Numpyで大きなデータを処理する場合、del
が擬似手動メモリ管理に役立つことがわかりました。例えば:
for image_name in large_image_set:
large_image = io.imread(image_name)
height, width, depth = large_image.shape
large_mask = np.all(large_image == <some_condition>)
# Clear memory, make space
del large_image; gc.collect()
large_processed_image = np.zeros((height, width, depth))
large_processed_image[large_mask] = (new_value)
io.imsave("processed_image.png", large_processed_image)
# Clear memory, make space
del large_mask, large_processed_image; gc.collect()
これは、Python GCが維持できない場合にシステムが狂ったようにスワップするため、スクリプトを停止することと、十分なヘッドルームを残してゆるいメモリしきい値を下回って完全にスムーズに実行することとの違いですマシンを使用して、動作中に参照およびコーディングします。
もう1つのニッチな使い方: pyroot ROOT5またはROOT6では、 "del"はもはや存在しないC++オブジェクトを参照しているpythonオブジェクトを削除するのに役立ちます。これはpyrootの動的な検索が同じ名前のC++オブジェクトを見つけてそれをpythonの名前に結び付けることを可能にします。したがって、次のようなシナリオがあります。
import ROOT as R
input_file = R.TFile('inputs/___my_file_name___.root')
tree = input_file.Get('r')
tree.Draw('hy>>hh(10,0,5)')
R.gPad.Close()
R.hy # shows that hy is still available. It can even be redrawn at this stage.
tree.Draw('hy>>hh(3,0,3)') # overwrites the C++ object in ROOT's namespace
R.hy # shows that R.hy is None, since the C++ object it pointed to is gone
del R.hy
R.hy # now finds the new C++ object
うまくいけば、このニッチはROOT7のsanerオブジェクト管理で閉じられるでしょう。
"del"コマンドは、配列内のデータを制御するのに非常に役立ちます。次に例を示します。
elements = ["A", "B", "C", "D"]
# Remove first element.
del elements[:1]
print(elements)
出力:
['B'、 'C'、 'D']