今、話題の人工知能(AI)などで人気のPython。初心者に優しいとか言われていますが、全然優しくない! という事を、つらつら、愚痴っていきます

003.Pythonの変数1

»

初回:2018/08/22

0.今回のまとめ
 Pythonの変数は動的型付けなので、変数に値を代入することで変数定義と型が決まります。それだけに、注意深く扱う必要があります。

1.変数定義
 Pythonでは、変数に値を代入することが、変数定義になります。(※1

>>> a = 100
>>> b = c = 200
>>> d,e = 300,400
>>> A = "100"
>>> B = C = '200'

 変数の大文字、小文字は、識別されますので、a と A は、異なる変数です。
で、下記の計算結果は、
>>> c = a + b + c
>>> c
500

となります。
では、
>>> C = A + B + C
は、どうなるでしょうか?
>>> C
'100200200'

となります。
いきなり、混乱させるような例(※2)ですが、文字列の連結は、+ 記号で行えます。

では、下記はどうなるでしょうか?

>>> D = a + B + c
「ごめん。無理。」(※3

結構、あいまいそうに見えて、数字と文字を混ぜると、怒られます。
例えば、
int d = a + B + c (100 + '200' + 500)⇒ 800
になり、
str D = a + B + c (100 + '200' + 500)⇒ '100200500'
になるなら、判りやすかった(※4)と思います。

例えば、次の関数を定義(※5)します。

>>> def plus( a,b ):
... return a + b
...
>>> print( plus(a,b ) )
300
>>> print( plus(A,B ) )
100200
>>> print( plus(A,b ) )
「あなたって、いつもそう。何度言ったら判るの?」(※6

さて、最後に(※7)、こういう変数の定義をした場合はどうなるか。

>>> def minus( a,b ):
... if( a > b ) :
... c = a - b
... elif( a < b ) :
... c = b - a
... return c
...
>>> minus( 100,200 )
100
>>> minus( 300,200 )
100
>>> minus( 300,300 )
「全然わかってないわね。」(※8

 わざと、同値のケースを書かなかったのですが、
実行するまで、変数cが未定義ということが分かりません。
最初に、c=0 と書くくらいなら、代入で変数定義という
やり方は、便利どころか、プログラムバグを含みやすい
構造といえます。

======= <<注釈>>=======

※1 変数定義
 参考URL
  https://www.sejuku.net/blog/25210
  【Python入門】変数の宣言と使い方を解説

※2 混乱させるような例
 ワザとです。(^^)

※3 「ごめん。無理。」
>>> D = a + B + c
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>>

※4 判りやすかった
 少し手間かもしれませんが、変数宣言と静的定義は必要と思っています。
 少なくとも、Pythonが、判りやすいとか、同じ書き方ができるとかは、
 まやかしです。結構書き手の能力に左右されます。

※5 関数を定義
 また後で、詳しく説明します。とりあえず、今はこんな感じです。
 興味がある方は、下記のURLを参照してみてください。

  https://qiita.com/icoxfog417/items/c17eb042f4735b7924a3
  Pythonではじまる、型のある世界

※6 「あなたって、いつもそう。何度言ったら判るの?」
>>> print( plus(A,b ) )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in plus
TypeError: can only concatenate str (not "int") to str
>>>

※7 最後に
 最後かい! というツッコミが。

※8 「全然わかってないわね。」
>>> minus( 300,300 )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in minus
UnboundLocalError: local variable 'c' referenced before assignment

======= <<参考URL>>=======

https://www.sejuku.net/blog/25210
【Python入門】変数の宣言と使い方を解説

https://www.sejuku.net/blog/23339
【Python入門】これだけは覚えておこう!Pythonの基本まとめ

https://docs.python.jp/3/library/typing.html
26.1. typing 型ヒントのサポート

https://qiita.com/icoxfog417/items/c17eb042f4735b7924a3
Pythonではじまる、型のある世界

http://programming-study.com/technology/python-master-variables/
Python超入門その8?変数の使い方をマスターしよう?

Comment(4)

コメント

@shiracamus

少し不正確だと思ったのでコメントします。
 
> Pythonの変数は動的型付けなので、変数に値を代入することで変数定義と型が決まります。
 
値を書いた時点で値の型が決まり、値を格納するためのメモリ(オブジェクト)領域が確保され、値が格納されます。
 
$ python
Python 3.6.4 (default, Jan 7 2018, 17:45:56)
[GCC 6.4.0] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> type(100)
#100という値(オブジェクト)自身がint型だと知っている
>>> id(100) #100という値(オブジェクト)のメモリアドレスを調べる
1728177616 #100という値(オブジェクト)のメモリアドレス
>>> hex(id(100))
'0x6701e5d0' #16進数にした方がアドレスだと認識しやすいですかね
 
変数は、その値(オブジェクト)の領域に名前を付けるだけのことです。
 
>>> a = 100
>>> hex(id(a))
'0x6701e5d0' #100という値のメモリ番地を覚えている
 
int型はイミュータブル(不変)オブジェクト、つまり定数なのでメモリの中の値を書き換えることはできません。
 
>>> a = 999
 
で別の値を代入すると、100とは別のメモリ上に 999 という値が格納されたint型オブジェクトが生成され、そのアドレスにaという名前を付け替えます。
 
>>> hex(id(999))
>>> hex(id(a))
'0x7fe529d0' #100とは違う999という値(オブジェクト)のメモリアドレスに変更
 
> 結構、あいまいそうに見えて、数字と文字を混ぜると、怒られます。
 
数値と文字を加算したときに、JavaやJavaScriptのように文字列連結する言語もあれば、PHPのように数値に変換してから算術加算する言語もあります。
Pythonでは、どっちにするか自分で決めてくれということなのでしょう。
ちなみに、数字と文字を混ぜた演算は、乗算ならできます。
 
>>> 3 * 'abc'
'abcabcabc'

ちゃとらん

@shiracamus さん。
詳しい説明、ありがとうございます。

これ、説明を受けて、初めて、その不可思議さに、おののいています。
# 不可思議といっても、自分の知っている概念の中で、遭遇したことがないだけです。

まず、びっくりなのが、数値も、オブジェクトなんですね。しかも、使い捨ての。
a=100 とすると、a という名前をつけたアドレスに、100 という数値を格納する、イメージでした。なので、Javaで言うところの、a++ みたいな演算をすると、そのアドレス内で、加算された値を格納するだけなのに、なぜ、Python では、出来ないんだろう、と思っていました。

>>> a=100
>>> b=10*10
>>> c=[100,100]
>>> id(a)
140705103536256
>>> id(b)
140705103536256
>>> id(c)
2001018577544
>>> id(c[0])
140705103536256
>>> id(c[1])
140705103536256
>>> c.append(5*20)
>>> id(c)
2001018577544
>>> id(c[2])
140705103536256
>>>

うおぉぉぉぉぉ
100 という数字は、すべて同じアドレスを指していますね。
ちなみに、リストは、オブジェクトという認識があるので、違和感はないです。

判れば判るほど、判らなくなる「不思議ちゃん」ですね。Pythonって娘は。

@shiracamus

-5~256のint値はキャッシュされ、常に同じアドレスになります。
それ以外の値は毎回生成します。
 
>>> id(999)
2145725184
>>> id(999)
2145725200
>>> id(999)
2145725216
 
ただし、 Pythonスクリプトファイルを実行する場合は、Pythonインタープリタが同じ数値リテラルならひとつのオブジェクトを共有するように中間コードに変換しますので、同じアドレスになったりします。

@shiracamus

ちなみに、Pythonの変数は辞書データになってます。変数名は辞書のキー、単なる文字列扱いです。
グローバル変数辞書 globals() とローカル変数辞書 locals() があります。
グローバル変数辞書を直接変更することもできます。
 
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': }
>>> a = 100
>>> globals()
{'a': 100, '__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': }
>>> globals()['a']
100
>>> globals()['a'] = 999 #変数辞書を直接変更
>>> a #変数の参照先が変更されている
999
 
locals() は、Python3で辞書のコピーを返すように仕様変更されたため、いたずらできないです。

コメントを投稿する