Python:関数・スコープ・クロージャ・クラス・参照カウンタ

その買うを、もっとハッピーに。|ハピタス

このサイトを参考にしながら、Python programmingの基礎である関数(function)、スコーピング(scoping)、クロージャ(closure)、クラス(class)、参照カウンタ(reference counter)についての学習を試みる。

スポンサーリンク

Functions and scoping

x = 100
def foo(y):
    return x + y
z = foo(7)
print('x:', x, '\nfoo:', foo, '\nz:', z)
x: 100 
foo: <function foo at 0x7f4561fd8f28> 
z: 107
def bar(x):
    x = 1000
    return foo(7)
w = bar('hi')
print('x:', x, '\nw:', w)
x: 100 
w: 107

Importantly, foo “remembers” that it was created in the global environment, so looks in the global environment to find a value for ‘x’. It does NOT look back in its “call chain”; rather, it looks back in its parent environment.
重要なのは、fooはグローバル環境で作られたことを覚えているということで、従って、グローバル環境を参照してxの値を探し出す。fooは、自身のコールチェーンは参照せず、代わりに自身の親環境を参照する。

スポンサーリンク

Optional arguments and default values

def foo(x, y = []):
    y = y + [x]
    return y

a = foo(7)
b = foo(88, [1, 2, 3])
print('a:', a, '\nb:', b)
a: [7] 
b: [1, 2, 3, 88]
c = foo(10)
print('a:', a, '\nb:', b, '\nc:', c)
a: [7] 
b: [1, 2, 3, 88] 
c: [10]
c = foo(10)
print('a:', a, '\nb:', b, '\nc:', c)
a: [7] 
b: [1, 2, 3, 88] 
c: [10]

Let’s try something that looks close to the same thing… but with an important difference!
以下では上と同じようなことをやっているように見えるが重要な違いがある。

def foo(x, y = []):
    y.append(x)   # different here
    return y

a = foo(7)
b = foo(88, [1, 2, 3])
print('a:', a, '\nb:', b)
a: [7] 
b: [1, 2, 3, 88]

Okay, so far it looks the same as with the earlier foo.
ここまではさっきのfooと同じように見える。

c = foo(10)
print('a:', a, '\nb:', b, '\nc:', c)
a: [7, 10] 
b: [1, 2, 3, 88] 
c: [7, 10]
c = foo(10)
print('a:', a, '\nb:', b, '\nc:', c)
a: [7, 10, 10] 
b: [1, 2, 3, 88] 
c: [7, 10, 10]

So quite different… all kinds of aliasing going on. Perhaps surprisingly, the default value to an optional argument is only evaluated once, at function definition time. The moral here is to be VERY careful (and indeed it may be best to simply avoid) having optional/default arguments that are mutable structures like lists… it’s hard to remember or debug such aliasing!
さっきとは違って、諸々のエイリアシングが発生している。多分、意外なのは、任意の引数への初期値が関数定義時に一度だけ求められているということだ。ここでの教訓は、リストのような可変構造のオプショナル/デフォルト引数を持つことにはかなりの注意が必要になり(もちろん、ただ単に使用を控えるのが一番なのだが)、そういったエイリアシングは、記憶もしくはデバッグを困難にさせる。

スポンサーリンク

Closures

def add_n(n):
    def inner(x):
        return x + n
    return inner
add1 = add_n(1)
print(add1(7))

add2 = add_n(2)
print(add2(3))

print(add1(77))
print(add_n(8)(9))

print(add2(77))
print(add_n(1)(-2))
8
5
78
17
79
-1
スポンサーリンク

Classes

A look ahead. We’ll refresh ourselves on classes later, but you might want to try these on your own now.
クラスについては将来的に講義で補う。

x = 'global var'
class Simple:
    x = 'class var'
    
print(Simple.x)
class var
x = 'global var'
class Simple:
    x = 'class var'
    
s = Simple()
print(s.x)
class var
x = 'global var'
class Simple:
    x = 'class var'
    
s = Simple()
s.x = 'instance var'
print(s.x)
print(Simple.x)
instance var
class var
x = 'global var'
class Simple:
    x = 'class var'
    def __init__(self):
        x = 'local var'
    
s = Simple()
print(s.x)
class var
x = 'global var'
class Simple:
    x = 'class var'
    def __init__(self):
        x = 'local var'
        self.x = 'instance var'
    
s = Simple()
print(s.x)
instance var
x = 'global var'
class Simple:
    x = 'class var'
    def __init__(self):
        x = 'local var'
        self.x = 'instance var'
    def which_x(self):
        return x
    
s = Simple()
print(s.which_x())
global var
スポンサーリンク

Reference Counting

This is an advanced feature you don’t need to know about, but you might be curious about. Python knows to throw away an object when its “reference counter” reaches zero. You can inspect the current value of an object’s reference counter with sys.getrefcount.
この機能については上級者向けなので知る必要はないのだが、この機能に関して興味を持つかもしれない。参照カウンターがゼロに達すると、Pythonはオブジェクトを捨てるようになっている。sys.getrefcountを使うことで、オブジェクトの参照カウンターの現在値を調べる事ができる。

import sys
L1 = [1, 2, 3]
print(sys.getrefcount(L1))
L2 = L1
print(sys.getrefcount(L1))
L3 = [L1, L1, L1]
print(sys.getrefcount(L1))
L3.pop()
print(sys.getrefcount(L1))
L3 = 7
print(sys.getrefcount(L1))
2
3
6
5
3
abc = 0
print(sys.getrefcount(123))
abc = 123
print(sys.getrefcount(123))
16
17
スポンサーリンク
スポンサーリンク