Блоґ одного кібера

Історія хвороби контуженого інформаційним вибухом

Python 2, nonlocal and immutable variables

with 6 comments

Нудний вступ я опущу, і перейду зразу до розваги:

def case(func): # декоратор прикладу
    print func.__name__ # печатаємо назву
    try:
        func() # виконуємо функцію
    except Exception, e: # Якщо є помилки
        print 'Error:', e # помічаємо і продовжуємо
    print

@case
def test_a():
    x = 1
    def inner():
        x = 2 # x в цій області імен посилається на новий об’єкт - 2
        print 'inner:', x # x == 2
    inner()
    print 'outer:', x # x == 1. Цю область імен ми не чіпали

@case
def test_b():
    x = [1]
    def inner():
        x[0] = 2 # при обчисленні x[0], ми шукаємо x у всіх просторах тут і вище
        print 'inner:', x # x == [2], x посилається на все той же об’єкт, але він змінився
    inner()
    print 'outer:', x # x == [2], сказано ж, об’єкт змінився.

@case
def test_c():
    x = 1
    def inner():
        x +=1 # генерує UnboundLocalError
        # бо x - в лівій частині присвоєння, що робить його локальним.
        # а в правій частині - x + 1, значення якого ще не визначено
        # бо ж x щойно з’явився в просторі імен
        # детальніше про те чому так: http://eli.thegreenplace.net/2011/05/15/understanding-unboundlocalerror-in-python/
        print 'inner:', x
    inner()
    print 'outer:', x # сюди ми не доходимо

# Як реалізувати нелокальну змінну, якщо ви працюємо не з третім Python?
@case
def test_d():
    # використати mutable об’єкт
    nonlocal = lambda:7 # функція підходить 
    nonlocal.x = 1
    def inner():
        nonlocal.x += 1
        print 'inner:', nonlocal.x # == 2
    inner()
    print 'outer:', nonlocal.x # == 2

Мораль теж опущу, все таки потрібно працювати. 😉

Advertisements

Written by bunyk

Травень 30, 2012 at 14:03

Оприлюднено в Кодерство

Tagged with

Відповідей: 6

Subscribe to comments with RSS.

  1. Хм.. я бы моделировал замыкания передачей доп. параметров функции inner

    def inner(world):
        world.x += 1
    

    Хотя подозреваю, что это не самое лучшее решение.

    jtimv

    Травень 30, 2012 at 14:55

    • Але я напевне даремно опустив мораль всіх експериментів: заміна об’єкта на який посилається дане ім’я веде за собою перетворення імені на локальне відносто поточного простору імен.Якщо вам потрібно змінювати якесь значення ззовні – слідкуйте за тим щоб об’єкт за ним не замінювався новим. Це можливо тільки коли він mutable..

      А передача параметра впринципі працює аналогічно, значення повернеться назад тільки якщо воно всередині mutable об’єкта. Інакше – створиться нова локальна версія, невидима ззовні.

      bunyk

      Травень 30, 2012 at 15:07

      • Точно. Практически полная аналогия с передачей параметра.

        def inner():
            world.x += 1 # внешний мир заметит
            world = {'a': 1, 'b': 2} # внешний мир не заметит
            world['a'] = 0; # внешний мир не заметит
        

        [trollmode ]Я даже не знаю, но наверное это хорошо, что я не на Python программирую 😉 [/trollmode]

        jtimv

        Травень 30, 2012 at 15:44

        • В кожній мові потрібно знати деякі речі аби випадково не накодити якусь х*ню. З тих з якими я знайомий. (Хаскель туди не входить). 🙂

          Але головне – добре що не на Python 2.

          bunyk

          Травень 30, 2012 at 17:43

  2. Треш. А чому кругом уточнення “Python 2”? Для третього там щось інше?

    danbst

    Травень 31, 2012 at 06:56

    • В третьому є ключове слово nonlocal, яке працює так само як global, але global шукає тільки в глобальному просторі імен:

      def foo():
          x = 2
          def bar():
              global x
              x += 2
              print x
          bar()
      
      foo()
      # OUT: Traceback (most recent call last):
      # OUT:   File "<input>", line 1, in <module>
      # OUT:   File "<input>", line 7, in foo
      # OUT:   File "<input>", line 5, in bar
      # OUT: NameError: global name 'x' is not defined
      

      В той час як nonlocal просто каже що змінну просто потрібно шукати десь вище:

      def foo():
          x = 2
          def bar():
              nonlocal x
              x += 2
              print(x)
          bar()
          
      foo()
      # OUT: 4
      

      bunyk

      Травень 31, 2012 at 10:53


Залишити відповідь

Заповніть поля нижче або авторизуйтесь клікнувши по іконці

Лого WordPress.com

Ви коментуєте, використовуючи свій обліковий запис WordPress.com. Log Out / Змінити )

Twitter picture

Ви коментуєте, використовуючи свій обліковий запис Twitter. Log Out / Змінити )

Facebook photo

Ви коментуєте, використовуючи свій обліковий запис Facebook. Log Out / Змінити )

Google+ photo

Ви коментуєте, використовуючи свій обліковий запис Google+. Log Out / Змінити )

З’єднання з %s

%d блогерам подобається це: