Как доказать, что оценка параметров выполняется" слева направо " в Python?
Например, в JavaScript мы могли бы написать такую программу:
var a = 1;
testFunction(++a, ++a, a);
function testFunction(x, y, z){
document.writeln("<br />x = " + x);
document.writeln("<br />y = " + y);
document.writeln("<br />z = " + z);
}
И мы получим результат:
x = 2
y = 3
z = 3
Это означает, что параметры действительно оцениваются слева направо в JavaScript. В C мы получим выход
x = 3
y = 3
z = 3
Мне было интересно, можем ли мы сделать то же самое в Python или это невозможно, так как это язык ссылок на значения pass by?
Я сделал простую программу, но не думаю, что это что-то доказывает:
x = 2
def f(x, y, z):
print(x, y, z)
f(x*2, x*2, x**2)
print(x)
4 4 4
2
Python не будет позвольте мне выполнить любое новое назначение в параметре функции, когда я вызываю его (например, f(x=4, x, x)
или что-то подобное).
6 ответов:
Разберите вызов функции.
>>> def foo(): ... bar(x+1, x+2, x+3) ... >>> dis.dis(foo) 2 0 LOAD_GLOBAL 0 (bar) 3 LOAD_GLOBAL 1 (x) 6 LOAD_CONST 1 (1) 9 BINARY_ADD 10 LOAD_GLOBAL 1 (x) 13 LOAD_CONST 2 (2) 16 BINARY_ADD 17 LOAD_GLOBAL 1 (x) 20 LOAD_CONST 3 (3) 23 BINARY_ADD 24 CALL_FUNCTION 3 27 POP_TOP 28 LOAD_CONST 0 (None) 31 RETURN_VALUE
Использование Python 3:
>>> a = [] >>> f = print( a.append(1), a[:], a.append(2), a[:], a.append(3), a[:] ) None [1] None [1, 2] None [1, 2, 3]
Архив:
>>> a = [] >>> f = print(a.append(1), a, a.append(2), a, a.append(3), a)
Довольно любопытно (на первый взгляд), что этот код производит:
None [1, 2, 3] None [1, 2, 3] None [1, 2, 3]
Однако
dis(f)
делает это более ясным:>>> dis(f) 1 0 LOAD_NAME 0 (print) #Loads the value of 'print' into memory. Precisely, the value is pushed to the TOS (Top of Stack) --> 3 LOAD_NAME 1 (a) #Loads the value of object 'a' 6 LOAD_ATTR 2 (append) #Loads the append attribute (in this case method) 9 LOAD_CONST 0 (1) #Loads the constant 1 12 CALL_FUNCTION 1 #a.append(1) is called 15 LOAD_NAME 1 (a) #for print(...,a,...) 18 LOAD_NAME 1 (a) #for the next a.append() 21 LOAD_ATTR 2 (append) 24 LOAD_CONST 1 (2) 27 CALL_FUNCTION 1 #a.append(2) 30 LOAD_NAME 1 (a) 33 LOAD_NAME 1 (a) 36 LOAD_ATTR 2 (append) 39 LOAD_CONST 2 (3) 42 CALL_FUNCTION 1 #a.append(3) 45 LOAD_NAME 1 (a) #loads a to be used thrice by print 48 CALL_FUNCTION 6 #calls print 51 PRINT_EXPR #prints TOS and clears it 52 LOAD_CONST 3 (None) #Loads None 55 RETURN_VALUE #Returns None
Результат
dis(f)
- это то, что мы ожидали-оценка от L до R. По существу, это "несоответствие" является следствием того, чтоprint()
оценивается последним. К этому времени значениеa
изменяется на[1, 2, 3]
, и один и тот же конечный объект печатается трижды.Если заменить
a
наa[:]
, то получим ожидаемый результат.
Здесь может помочь пользовательский класс:
class Tester(object): "test object to reveal left to right evaluation" def __init__(self, value): self.value = value def __add__(self, value): print("adding ", value) return Tester(self.value + value) def __repr__(self): return repr(self.value)
А при запуске:
--> t = Tester(7) --> t 7 --> t = t + 7 adding 7 --> t 14 --> def blah(a, b, c): ... print(a, b, c) ... --> blah(t+1, t+2, t+3) adding 1 adding 2 adding 3 15 16 17
Это тоже показывает ИМХО:
>>> '{} {} {}'.format(x,x+1,x+2) '1 2 3'
Правка:
>>> def f(t): return time.time()-t ... >>> t1=time.time(); '{:.4} {:.4} {:.4}'.format(f(t1),f(t1),f(t1)) '5.007e-06 7.868e-06 9.06e-06'
Короткий ответ: слева направо
Пример: Поскольку это вопрос, который всегда всплывает в моей голове, когда я делаю арифметические операции (должен ли я преобразовать в float и какое число), пример из этого аспекта представлен:
>>> a = 1/2/3/4/5/4/3 >>> a 0
Когда мы делим целые числа, неудивительно, что они округляются ниже.
>>> a = 1/2/3/4/5/4/float(3) >>> a 0.0
Если мы наберем последнее целое число для float, мы все равно получим ноль, так как к тому времени, когда наше число будет разделено на float, оно уже станет 0 из-за целочисленное деление.
>>> a = 1/2/3/float(4)/5/4/3 >>> a 0.0
Тот же сценарий, что и выше, но смещение типа float немного ближе к левой стороне.
Наконец, когда мы набираем первое целое число для float, результат является желаемым, так как начиная с первого деления, т. е. самого левого, мы используем floats.>>> a = float(1)/2/3/4/5/4/3 >>> a 0.0006944444444444445
Extra 1: Если вы пытаетесь ответить на этот вопрос, чтобы улучшить арифметическую оценку, вы должны проверить Это
Экстра 2: Пожалуйста, будьте осторожны со следующим сценарий:
>>> a = float(1/2/3/4/5/4/3) >>> a 0.0