coLinux日記

coLinuxはフリーソフトを種として、よろずのシステムとぞなれりける。

SimplePrograms で Python を学ぶ + 代入文について

2024-07-09 22:27:49 | Python
SimplePrograms の 17番目のプログラムで、気になる代入の形が出てきたので少し調べてみました。

Python の場合、代入は「代入文 (Assignmet statement) 」で規定されているようです。
https://docs.python.org/ja/3/reference/simple_stmts.html#assignment-statements

「文」だとすると

left = right = col

は出来ないと思えます。実際 JavaScript などは、代入は「式」となっています。

そこで、もう一度定義をじっくり見てみました。(インデントがいい加減ですのでご容赦ください。)

assignment_stmt ::= (target_list "=")+ (starred_expression | yield_expression)
target_list    ::= target ("," target)* [","]
target        ::= identifier
          | "(" [target_list] ")"
          | "[" [target_list] "]"
          | attributeref
          | subscription
          | slicing
          | "*" target

starred_expression ::= expression | (starred_item ",")* [starred_item]
starred_item ::= assignment_expression | "*" or_expr


ちゃんと見てみたら、assignment_stmt に定義されていました。
(target_list "=")+
なので、
target_list1 = target_list2 = .....
となって、代入文として連鎖代入ができるのですね。

さらに、target_list が、
target ("," target)* [","]
となっているので、
target1 , target2 , target3 .... = .......
が可能なので、

left, right = left - 1, right + 1

も出来るのですね。これは 4 lines プログラムでも出てきたのですが、右側がタプルだったのでそんなものかと勘違いしていました。

改めて、試して見ましょう。
連鎖代入は、
>>> a = b = c = 4
>>> print(a, b, c)
4 4 4


target が複数の場合は、
>>> a , b = 5 , 6
>>> print(a, b, c)
5 6 4
>>>


target は、丸括弧や、角括弧で囲めるので、
>>> (a , b ) = 7, 8
>>> print(a,b,c)
7 8 4
>>> [a , b ] = 9, 10
>>> print(a,b,c)
9 10 4
>>>


更に、定義どおり最後に "," があっても大丈夫です。
>>> a, b, c, = 10, 20, 30
>>> print(a,b,c)
10 20 30
>>> a, b, c, = 100, 200, 300,
>>> print(a,b,c)
100 200 300
>>>


target_list は、左から右へ順番に代入されるようで、target が添字表記(subscription) の時、良くわかります。
>>> print(d)
[1, 2, 3, 4, 5]
>>> i, d[ i ] = 2 , 30
>>> print(d)
[1, 2, 30, 4, 5]
>>>


"=" の右側は、4 lines プログラムのようにタプルでも良いので、リストも良いみたいです。
>>> a , b, c = 1, 2, 3
>>> a, b = (10, 20)
>>> print(a,b,c)
10 20 3
>>> a, b = [100, 200]
>>> print(a,b,c)
100 200 3

この代入するタプルやリストは、予め変数に代入しておくことが可能です。
>>> print(a, b, c)
100 200 3
>>> d = [1, 2]
>>> print(d)
[1, 2]
>>> a, b = d
>>> print(a, b, c)
1 2 3
>>>

ついでに、target がスライス表記(slicing) の場合を見てみます。
>>> d = [1, 2, 3, 4, 5, 6]
>>> d[1:3]
[2, 3]
>>> d[2:4]
[3, 4]
>>> d[2:4] = 30, 40
>>> print(d)
[1, 2, 30, 40, 5, 6]
>>> d[0:2]
[1, 2]
>>>

通常は、「最初の添字:最後の添字」となりそうですが、Python の場合は違いますね。
これは、文字列のインデックス表記と同じで、
https://docs.python.org/ja/3/tutorial/introduction.html#text
から引用すると、

----------
インデクス表記に加え、 スライス もサポートされています。インデクス表記は個々の文字を取得するのに使いますが、 スライス を使うと部分文字列を取得することができます:
>>> word = 'Python' # これは追記しました。
>>> word[0:2] # characters from position 0 (included) to 2 (excluded)
'Py'
>>> word[2:5] # characters from position 2 (included) to 5 (excluded)
'tho'

スライスの使い方をおぼえる良い方法は、インデックスが文字と文字の あいだ (between) を指しており、最初の文字の左端が 0 になっていると考えることです。そうすると、 n 文字からなる文字列中の最後の文字の右端はインデックス n となります。例えばこうです:

+---+---+---+---+---+---+
| P  |  y  |  t  |  h |  o |  n |
+---+---+---+---+---+---+
0    1     2     3    4    5    6
-----------

つまり、インデクスは要素の間を表し、この図で言えば、
[a:b]なら[インデクスの右側の文字:インデクスの左側の文字]なので、リストなら、
a の右側の要素から、b の手前の要素までを表すようです。

そのため、d[2:4] なら、先頭要素を0番目として、2番目の要素から3番目の要素までを表すのですね。

定義の最後によると、 target の頭に "*" を付けることができるようです。しかし、

>>> *c = [1, 2, 3]
File "<stdin>", line 1
SyntaxError: starred assignment target must be in a list or tuple
>>>


なので、色々試したことろ、次のようにできることが分かりました。

>>> a, b, *c = [1, 2, 3, 4, 5, 6]
>>> print(a, b, c)
1 2 [3, 4, 5, 6]
>>>
>>> a, b, *c = 1, 2, 3, 4, 5
>>> print(a, b, c)
1 2 [3, 4, 5]
>>>
>>> a, b, *c = (1, 2, 3, 4, 5)
>>> print(a, b, c)
1 2 [3, 4, 5]
>>>


興味深いです。ちなみに、代入文で詳しそうなのを探したら、以下のものを見つけました。

Python's Assignment Operator: Write Robust Assignments
https://realpython.com/python-assignment-operator/

(中学生や高校生の方は、英語なので昔だったら諦めるところですが、現在はブラウザで翻訳できるので敷居が低くなりました。)

これからも分かるように、Python の「代入」は奥が深そうですね。

今回は、「代入文」だけになりました。次回は「リスト内包表記」に触れてみたいと思います。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする