[py] забавно

pilot

Что-то такое имхо можно спрашивать на собеседовании:

foo = 'a'
def fn:
print foo
# foo = 'b'
fn

Не запуская, сказать:
Что выдаст такой скрипт?
Что выдаст такой скрипт, если раскомментировать строчку? (И главное, где :) )
Для меня было новостью :o

amiantos

:grin: :grin:
Куда хоть собеседование было? И на какую должность?

pilot

Да не было никакого, просто такая вроде бы простая вещь:
есть у питона внутреннее представление, и там он не совсем скриптовый.
А простую вещь где еще спрашивать :)
P.S. То бишь сам нашел

tipnote

foo = 'a'
def fn:
print foo
global foo
foo = 'b'
fn

Сработает... А что такое "не совсем скриптовый"?

pilot

Сработает

Да я знаю как сработает. Проблема не в "как написать" :)
Если

def main:
foo = True
def fn:
foo = False
fn

И global не поможет, поможет [True], например. Тут дело становится в типе и связывании.
global еще и некошерный :)
Не совсем скриптовый — значит "вперед" подсматривает. Обычно же в документации рассказывают про line физические и логические, мол скрипт по строкам выполняется.

tipnote

Ну, это вопрос скорее области видимости и closures. Вроде бы везде "обсосан" :D
А про скриптовость... думаю, большинство байткодовых скриптовых языков такие

pilot

 
Ну, это вопрос скорее области видимости и closures.

 

def first:
foo = True
def fn:
foo = False
fn
print foo

def second:
foo = [True]
def fn:
foo[0] = False
fn
print foo

области видимости отличаются? :)
 
большинство байткодовых скриптовых языков такие

Я не знал :(
Ну и пример простой, мне понравился.

tipnote

Ну, проблема касается именно локальных переменных. Поэтому область видимости. А проблема в оптимизации локальных переменных на этапе генерации байткода. Проблема известна давно, еще в python warts описана.

zontik

А проблема в оптимизации локальных переменных на этапе генерации байткода.
Не вижу тут проблемы в оптимизации локальных переменных на этапе генерации байткода
Each occurrence of a name in the program text refers to the binding of that name established in the innermost function block containing the use.
If the definition occurs in a function block, the scope extends to any blocks contained within the defining one, unless a contained block introduces a different binding for the name.
The following constructs bind names: ... and targets that are identifiers if occurring in an assignment, for loop header, or in the second position of an except clause header.
Each assignment or import statement occurs within a block defined by a class or function definition or at the module level (the top-level code block).
Это не проблема, это типа так язык устроен.
типа вот:

>>> dis.dis (first)
...

3 6 LOAD_CONST 1 (<code object fn at 0x8a673c8, file "<pyshell#4>", line 3>)
9 MAKE_FUNCTION 0 # сделали функцию без параметров
12 STORE_FAST 1 (fn)

5 15 LOAD_FAST 1 (fn)
18 CALL_FUNCTION 0
21 POP_TOP

...

>>> dis.dis (second)
2 0 LOAD_GLOBAL 0 (True)
3 BUILD_LIST 1
6 STORE_DEREF 0 (foo)

3 9 LOAD_CLOSURE 0 (foo)
12 BUILD_TUPLE 1 # типа одна свободная переменная
15 LOAD_CONST 1 (<code object fn at 0x8a72188, file "<pyshell#7>", line 3>)
18 MAKE_CLOSURE 0 # сделали замыкание без параметров
21 STORE_FAST 0 (fn)
...
>>>

оптимизация не при чем, вроде.

tipnote

Называй как хочешь. Я это называю оптимизацией, так как байт-код компилятор делает предположения на основе анализа всего блока кода, даже того, который к моменту выполнения определенных команд еще не выполнится.
Python's source-to-bytecode compiler tries to optimize accesses to local variables. It decides that a variable is local if it's ever assigned a value in a function. Without the assignment i = i + 1, Python would assume that i is a global and generate code that accessed the variable as a global at the module level. When the assignment is present, the bytecode compiler generates different code that assumes i is a local variable; local variables are assigned consecutive numbers in an array so they can be retrieved more quickly.

pilot

Вы вроде про разное, ты про первый кусочек кода говоришь, а — про последний.
Эти кусочки про разное:
первый про разбор кода функции, про который как раз я не знал, и про что ты привел цитату
второй про naming, который как раз везде разбирается и рассказывается.

pilot

Продолжение (это правда совсем для детского сада):

~/test fvv$ cat foo.py
import bar
print 'foo'
~/test fvv$ cat bar.py
import foo
print 'bar'

Что выдаст:
 ~/test fvv$ python foo.py 

и что
 ~/test fvv$ python
>>> import foo

tipnote

Ты злодей! :D
Я уж было решил, что чего-то не знаю и запустил интерпретатор проверить. И через пару минут обнаружил, что все я знаю :grin:
В общем, в тему треда "забавно" не попрет :p

bleyman

А объясни или дай ссылку на объяснение того, почему так, пожалуйста!

pilot

Если запускаешь скрипт из командной строки, то у него __name__ == "__main__".
При импорте модуль много раз не импортируется, интерпретатор хранит список импортированных модулей и импортирует только если в списке модуля еще нет.
В первом случае в списке нет foo, есть __main__, вот он foo и импортирует второй раз.
Во втором случае оба модуля нормальным образом оказываются в списке, импортируются по одному разу.
Как-то так :)

ppplva

class ClassAddMixin:
def _add(self, other):
self.__dict__.update(other.__dict__)
return self
__add__ = classmethod(_add)

class A(ClassAddMixin):
def f(self):
print "A.f"

class B(ClassAddMixin):
def g(self):
print "B.g"


AB = A + B
# AB = A.__add__(B)

ab = AB
ab.f
ab.g
А почему вот эта штука не работает?
TypeError: unsupported operand type(s) for +: 'classobj' and 'classobj'
Если сложение убрать и раскомментировать следующую строку, то все ок. Есть какие-то особенности перегрузки операторов в classobj?

nikita270601

Если ты хочешь складывать классы оператором +, объяви __add__ в их метаклассе.

tipnote

мое до исправления:
Для каждого модуля есть понятие import time code - код, работающий при импорте. Сюда
входит все, что описано вне классов/функций + создание классов/функций по их описаниям. Этот код выполняется единожды при первом импорте модуля. В случае запуска модуля как скрипта, импорт этого модуля не выполняется. Поэтому там по два вывода при импорте в интерпретаторе и три при запуске как скрипт. А порядок вывода определяется порядком первого импорта и вложенностью. Ну банально, короче.

На самом деле:
Для каждого модуля есть понятие initialization code. Сюда входит все, что описано вне классов/функций. Этот код выполняется единожды при первом импорте модуля. В случае запуска модуля как скрипта, импорт этого модуля не выполняется.
В последнем предложении я могу быть не прав. Я так и не смог выяснить, можно ли назвать создание __main__ модуля импортом. Впрочем, как мне уже намекнули, это религиозный вопрос.
Оставить комментарий
Имя или ник:
Комментарий: