2024年3月18日月曜日

言語ポエム pythonのscopeって独特じゃない?

今日もぱいぱいしているヒラサカです.みんなもぱいぱいしてるかい?

pythonのscopeって独特じゃない?
上位で既出の変数を下位で代入したら下位止まりでした.えーっそんなぁ...

最初はこんな感じで困りました.
def main()
    a = 0
    sub()
    print(a)   ←0のまま、えーっなんで?

    def sub()
        a=1 ←local扱いで短命で終わるのが原因

aをglobal変数として扱うには、こうすると動きます.
a=0

def main()
    global a ←localじゃなくてglobalのやつと言う
    sub()
    print(a)   ←1になってめでたし

    def sub()
        global a ←localじゃなくてglobalのやつと言う
        a=1

Cとかだと、何も言わなければglobal扱いですが、
pythonは、何も言わなければlocal扱いのようです.

ただねぇ、、、Cみたいに使える場面もありますよ.
例えばシリアルのハンドラportはこう書きます.
def main():
    port = serial.Serial('COM7', baudrate=115200, ......)

    def sub()
        s = port.readline() ←global的に読めはする

global portとか書かなくても、portを読むだけならどこでも読めるんでしょう.(推測)

まとめると、pythonのscopeはこうゆうことなわけ?
・既出変数はglobal的だが読めるだけ
・既出変数を上書きするとlocal扱いで、上位に波及しない
・読み書きglobal変数が欲しければ”global a”のように宣言する

ただの使いやすいscript言語だろと思ってたけど、違うとこもあるんだ.オロオロ...

かしこ

17 件のコメント:

  1. >ただの使いやすいscript言語だろと思ってたけど、違う
    この辺は、いわゆる
    ・Modern な、Object 指向言語
    は、皆同じですね。 (Javascript とかも同じ。私も時々ハマる。)
    ※実は、perl でも同じことが出来るのですが、非宣言変数は、デフォルトで、
    ・My が付いている
    (もちろん、perl のデフォルトでは、付きませんが。)
    とでも思ってくれればいいです。
    ※C言語的な考え方だと、関数自体が「一つの独立したオブジェクト」(モジュール)なので、変数に、
    ・EXTERN 宣言 を付けないと、他のモジュールの変数が見れない
    のに、類似してますね。
    ※「Modern な、Object 指向言語」の中には、「完全に、Global 変数禁止」な言語もあるので、そういう時は、名前は何でもいいですが、例えば「g 」というパブリッククラスを作って、そこでパブリック変数を宣言して「Global 変数代わり」(g.Variable とかで、何処でも参照可能になる)にすると言う、良く知られたテクニックがあります。
    (Object 指向的には邪道だけど、このやり方をしてないシステムは見たこと無い。)

    >Cとかだと、何も言わなければglobal扱い
    「関数の外」で、宣言すれば「そのモジュール内」なら、確かに「どこからでも参照」できますね。

    返信削除
    返信
    1. >g.Variable

      これはわたしも経験あり.

      削除
    2. mutable/immutableというのもCには無かったよな気がするとこです

      削除
    3. >Cには無かった
      です、確かに。
      でも、「C++」にはある(const なんたらと言うやつ)ので、現実的には、
      ・純粋な「C」環境は殆ど無い(gcc は、C++ だし)
      使う気になれば使えるんですよね・・・
      ※コメントの「//」も、「C++」からなので、ホントは「C では使えない」のですが、当たり前のように、 *.c のソースでみんな使ってるし(笑)

      削除
    4. /* これはめんどくさいのだっ */
      // これでいい

      削除
  2. >global portとか書かなくても、portを読むだけならどこでも読める
    この場合は、serial が 「特殊クラス(デバイスクラス)」なので、port も勝手にそれを継承して、
    ・どこでも見れる(デバイスは、システム固有だから。「関数内デバイス」とか、
    あり得ないし(笑))
    ようになるのでしょう (私もpython使いじゃないので、憶測ですが)

    返信削除
    返信
    1. >特殊クラス....どこでも見れる

      そうゆうのがあるですか

      削除
    2. >特殊クラス
      ウソです。単に、「pySerialモジュール」の、クラスですね。
      「serial.Serial()」って、単に class を new してるだけじゃないか。。。
      ※って、python って、「new」要らないのか!?謎な言語だな。
      多分中身は、OS の COM API を呼んでるだけだな。まぁ、
      そういう意味では「純粋なpython」じゃないから「特殊」かも知れませんが。

      削除
    3. new?
      そういえば使ってないです、初出の物はなんでもnewなのかしら
      /* しらんけど */
      /* あやふやな理解で言語をつかってるわたくし */

      削除
  3. murasaki
    多分なんですけど、この書き方だとsubが関数内定義の関数になっているかもです。期せずして結構難しい書き方になっているかもです。
    この書き方だと親の関数で定義した変数は子供の変数では読めるけど書き込めない。子供の変数は親からアクセスできない。になりそうです。

    変数は関数の外で書けばグローバル扱いになるのはc言語と一緒かもです。関数の中で定義するとローカルというのもc言語と一緒かと。

    関数で値を変えたい場合は返り値で返してあげないとだめかもですね。

    たとえばですが。
    def sub(a):
    a=1
    return a

    #メイン
    if __name__ == "__main__":
    a=0
    a=sub(a)
    print(a)

    ↑スマホで打ち込んでいるので動かないかもですが💦

    返信削除
    返信
    1. 参照渡しですね.
      /* 書けないなら読めないでよとか思うわたくし */
      もう~

      削除
    2. murasaki
      自分の場合、コピペばかりしているので、あまりわかっているわけではなく💦

      削除
    3. /* いまdrop down listで悩んでいます */

      削除
    4. ボタンが押されたeventのcallback()って引数が決まってるから参照渡しできないのかな?
      /* え~ん */

      しかしあれだね.関数返り値の宣言ってないんですねぱいぱいには.行き当たりばったりでなんでも返せる? えーっ?

      削除
    5. >行き当たりばったりでなんでも返せる?
      perl みたい(笑)

      削除
    6. 返さなくてもいいし、返したくなったら型とか無視して何を返してもOKみたいです.
      まぁあまりにも身勝手だと呼び出し側で困るでしょうけどw

      削除