Почти. Необходимо предложить «[» как матрицу из двух столбцов:
dat$matval <- mat[ cbind(dat$I, dat$J) ] # should do it.
Существует предостережение: хотя это также работает для данных, они сначала принудительно применяются к матричному классу, и если какие- числовая, вся матрица становится классом «наименьший знаменатель».
На самом деле, это не недостаток конструкции, и не из-за внутренних компонентов или производительности.
Это происходит просто из-за того, что функции в Python являются первоклассными объектами, а не только частью кода.
Как только вы начинаете думать таким образом, это становится полностью осмысленным: функция - это объект, являющийся оценивается по его определению; параметры по умолчанию являются своего рода "данными-членами", и поэтому их состояние может меняться от одного вызова к другому - точно так же, как и в любом другом объекте.
В любом случае, Effbot имеет очень хорошее объяснение причин такого поведения в Значения параметров по умолчанию в Python .
Мне он показался очень ясным, и я действительно предлагаю прочитать его, чтобы лучше понять, как работают объекты функций.
Самым коротким ответом, вероятно, будет «определение это исполнение ", поэтому весь аргумент не имеет строгого смысла. В качестве более надуманного примера вы можете процитировать следующее:
def a(): return []
def b(x=a()):
print x
Надеюсь, этого достаточно, чтобы показать, что невыполнение выражений аргументов по умолчанию во время выполнения оператора def
непросто или не имеет смысла. , или и то, и другое.
Я согласен, что это ошибка, когда вы пытаетесь использовать конструкторы по умолчанию.
Вы спрашиваете, почему это:
def func(a=[], b = 2):
pass
внутренне не эквивалентно этому:
def func(a=None, b = None):
a_default = lambda: []
b_default = lambda: 2
def actual_func(a=None, b=None):
if a is None: a = a_default()
if b is None: b = b_default()
return actual_func
func = func()
, за исключением случая явного вызова func (None, None), который мы ' будет проигнорировать.
Другими словами, почему бы вместо оценки параметров по умолчанию не сохранить каждый из них и не оценивать их при вызове функции?
Один ответ, вероятно, прямо здесь - он эффективно включит каждую функцию с параметры по умолчанию в закрытие. Даже если это все спрятано в интерпретаторе, а не полноценное закрытие, данные должны где-то храниться. Это будет медленнее и потребует больше памяти.
Это оптимизация производительности. В результате этой функциональности Как вы думаете, какой из этих двух вызовов функций быстрее?
def print_tuple(some_tuple=(1,2,3)):
print some_tuple
print_tuple() #1
print_tuple((1,2,3)) #2
Я дам вам подсказку. Вот разборка (см. http://docs.python.org/library/dis.html ):
#
1 0 LOAD_GLOBAL 0 (print_tuple)
3 CALL_FUNCTION 0
6 POP_TOP
7 LOAD_CONST 0 (None)
10 RETURN_VALUE
#
2 0 LOAD_GLOBAL 0 (print_tuple)
3 LOAD_CONST 4 ((1, 2, 3))
6 CALL_FUNCTION 1
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
Я сомневаюсь в Опытное поведение имеет практическое применение (кто действительно использовал статические переменные в C, не создавая ошибок?)
Как видите, дает выигрыш в производительности при использовании неизменяемых аргументов по умолчанию. Это может иметь значение, если это часто вызываемая функция или для создания аргумента по умолчанию требуется много времени. Также имейте в виду, что Python - это не C. В C у вас есть константы, которые практически бесплатны. В Python у вас нет этого преимущества.
org / library / dis.html ): #
1 0 LOAD_GLOBAL 0 (print_tuple)
3 CALL_FUNCTION 0
6 POP_TOP
7 LOAD_CONST 0 (None)
10 RETURN_VALUE
#
2 0 LOAD_GLOBAL 0 (print_tuple)
3 LOAD_CONST 4 ((1, 2, 3))
6 CALL_FUNCTION 1
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
Я сомневаюсь, что опытное поведение имеет практическое применение (кто действительно использовал статические переменные в C, без разведение ошибок?)
Как видите, дает выигрыш в производительности при использовании неизменяемых аргументов по умолчанию. Это может иметь значение, если это часто вызываемая функция или для создания аргумента по умолчанию требуется много времени. Также имейте в виду, что Python - это не C. В C у вас есть константы, которые практически бесплатны. В Python у вас нет этого преимущества.
org / library / dis.html ): #
1 0 LOAD_GLOBAL 0 (print_tuple)
3 CALL_FUNCTION 0
6 POP_TOP
7 LOAD_CONST 0 (None)
10 RETURN_VALUE
#
2 0 LOAD_GLOBAL 0 (print_tuple)
3 LOAD_CONST 4 ((1, 2, 3))
6 CALL_FUNCTION 1
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
Я сомневаюсь, что опытное поведение имеет практическое применение (кто действительно использовал статические переменные в C, без разведение ошибок?)
Как видите, дает выигрыш в производительности при использовании неизменяемых аргументов по умолчанию. Это может иметь значение, если это часто вызываемая функция или для создания аргумента по умолчанию требуется много времени. Также имейте в виду, что Python - это не C. В C у вас есть константы, которые практически бесплатны. В Python у вас нет этого преимущества.
Это может иметь значение, если это часто вызываемая функция или для создания аргумента по умолчанию требуется много времени. Также имейте в виду, что Python - это не C. В C у вас есть константы, которые практически бесплатны. В Python у вас нет этого преимущества. Это может иметь значение, если это часто вызываемая функция или для создания аргумента по умолчанию требуется много времени. Также имейте в виду, что Python - это не C. В C у вас есть константы, которые практически бесплатны. В Python у вас нет этого преимущества.Раньше я думал, что создание объектов во время выполнения будет лучшим подходом. Сейчас я менее уверен, поскольку вы теряете некоторые полезные функции, хотя, возможно, это того стоит, просто для предотвращения путаницы новичков. Недостатки этого:
1. Производительность
def foo(arg=something_expensive_to_compute())):
...
Если используется оценка времени вызова, дорогостоящая функция вызывается каждый раз, когда ваша функция используется без аргумента. Вы либо заплатите высокую цену за каждый вызов, либо вам придется вручную кэшировать значение извне, загрязняя ваше пространство имен и добавляя многословности.
2. Принудительная привязка параметров
Полезным трюком является привязка параметров лямбда к текущему связыванию переменной при создании лямбда. Например:
funcs = [ lambda i=i: i for i in range(10)]
Это возвращает список функций, которые возвращают 0,1,2,3 ... соответственно. Если поведение изменилось, они вместо этого привяжут i
к значению времени вызова i, так что вы получите список функций, которые все вернули 9
.
В противном случае единственный способ реализовать это - создать дополнительное замыкание с привязкой i, то есть:
def make_func(i): return lambda: i
funcs = [make_func(i) for i in range(10)]
3. Самоанализ
Рассмотрим код:
def foo(a='test', b=100, c=[]):
print a,b,c
Мы можем получить информацию об аргументах и значениях по умолчанию с помощью модуля inspect
, который
>>> inspect.getargspec(foo)
(['a', 'b', 'c'], None, None, ('test', 100, []))
Эта информация очень полезна для таких вещей, как создание документов, метапрограммирование, декораторы и т. Д. .
Теперь предположим, что поведение значений по умолчанию можно изменить так, чтобы оно было эквивалентно:
_undefined = object() # sentinel value
def foo(a=_undefined, b=_undefined, c=_undefined)
if a is _undefined: a='test'
if b is _undefined: b=100
if c is _undefined: c=[]
Однако мы потеряли способность к самоанализу и посмотреть, каковы аргументы по умолчанию . Поскольку объекты не были построены, мы можем ' я никогда не получаю их, фактически не вызывая функцию. Лучшее, что мы могли сделать, - это сохранить исходный код и вернуть его в виде строки.
This behavior is easy explained by:
So:
def x(a=0, b=[], c=[], d=0):
a = a + 1
b = b + [1]
c.append(1)
print a, b, c
a
doesn't change - every assignment call creates new int object - new object is printedb
doesn't change - new array is build from default value and printedc
changes - operation is performed on same object - and it is printedПричина проста в том, что привязки выполняются при выполнении кода, а определение функции выполняется, ну ... когда функции определены.
Сравните это:
class BananaBunch:
bananas = []
def addBanana(self, banana):
self.bananas.append(banana)
Этот код страдает точно такой же неожиданной случайностью. бананы - это атрибут класса, и, следовательно, когда вы добавляете к нему элементы, он добавляется ко всем экземплярам этого класса. Причина точно такая же.
Это просто «Как это работает», и заставить его работать по-другому в случае функции, вероятно, было бы сложно, а в случае класса, вероятно, невозможно, или, по крайней мере, сильно замедлить создание экземпляра объекта, поскольку вам пришлось бы хранить код класса и выполнять его при создании объектов.
Да, это неожиданно. Но как только копейка падает, она идеально подходит к тому, как работает Python в целом. Фактически, это ' это хорошее учебное пособие, и как только вы поймете, почему это происходит, вы будете лучше разбираться в Python.
Тем не менее, он должен занимать видное место в любом хорошем учебнике по Python. Потому что, как вы упомянули, каждый рано или поздно сталкивается с этой проблемой.
Я ничего не знаю о внутренней работе интерпретатора Python (и я не являюсь экспертом в компиляторах и интерпретаторах), поэтому не обвиняйте меня, если я предлагаю что-то непонятное или невозможное.
При условии, что объекты python изменяемы , я думаю, что это следует учитывать при разработке аргументов по умолчанию. Когда вы создаете список:
a = []
, вы ожидаете получить новый список, на который ссылается a
.
Почему a = []
в
def x(a=[]):
создавать новый список при определении функции, а не при ее вызове? Это похоже на то, как вы спрашиваете: «Если пользователь не предоставляет аргумент, то создает новый список и использует его, как если бы он был создан вызывающей стороной». Я думаю, что это неоднозначно: пользователь
def x(a=datetime.datetime.now()):
, хотите ли вы, чтобы для a
по умолчанию было установлено значение datetime, соответствующее тому, когда вы определяете или выполняете x
?
В этом случае, как и в предыдущем, я сохраню такое же поведение, как если бы аргумент по умолчанию «назначение» был первой инструкцией функции ( datetime.now ()
, вызываемой при вызове функции).
С другой стороны, если пользователь хочет отображение времени определения, он может написать:
b = datetime.datetime.now()
def x(a=b):
Я знаю, я знаю: это закрытие. В качестве альтернативы Python может предоставить ключевое слово для принудительной привязки времени определения:
def x(static a=b):
Suppose you have the following code
fruits = ("apples", "bananas", "loganberries")
def eat(food=fruits):
...
When I see the declaration of eat, the least astonishing thing is to think that if the first parameter is not given, that it will be equal to the tuple ("apples", "bananas", "loganberries")
However, supposed later on in the code, I do something like
def some_random_function():
global fruits
fruits = ("blueberries", "mangos")
then if default parameters were bound at function execution rather than function declaration then I would be astonished (in a very bad way) to discover that fruits had been changed. This would be more astonishing IMO than discovering that your foo
function above was mutating the list.
The real problem lies with mutable variables, and all languages have this problem to some extent. Here's a question: suppose in Java I have the following code:
StringBuffer s = new StringBuffer("Hello World!");
Map<StringBuffer,Integer> counts = new HashMap<StringBuffer,Integer>();
counts.put(s, 5);
s.append("!!!!");
System.out.println( counts.get(s) ); // does this work?
Now, does my map use the value of the StringBuffer
key when it was placed into the map, or does it store the key by reference? Either way, someone is astonished; either the person who tried to get the object out of the Map
using a value identical to the one they put it in with, or the person who can't seem to retrieve their object even though the key they're using is literally the same object that was used to put it into the map (this is actually why Python doesn't allow its mutable built-in data types to be used as dictionary keys).
Your example is a good one of a case where Python newcomers will be surprised and bitten. But I'd argue that if we "fixed" this, then that would only create a different situation where they'd be bitten instead, and that one would be even less intuitive. Moreover, this is always the case when dealing with mutable variables; you always run into cases where someone could intuitively expect one or the opposite behavior depending on what code they're writing.
I personally like Python's current approach: default function arguments are evaluated when the function is defined and that object is always the default. I suppose they could special-case using an empty list, but that kind of special casing would cause even more astonishment, not to mention be backwards incompatible.
Это может быть правдой, что:
это полностью согласовано придерживаться обеих вышеперечисленных функций и все же сделать еще одно замечание:
Другие ответы или, по крайней мере, некоторые из них либо делают пункты 1 и 2, но не 3, или сделать пункт 3 и преуменьшить значение пунктов 1 и 2. Но все три верны.
Возможно, смена лошадей на полпути здесь потребует значительных поломок и может возникнуть больше проблем. изменив Python на интуитивно понятную обработку начального фрагмента Стефано. И может быть правда, что кто-то, кто хорошо знал внутреннее устройство Python, мог объяснить минное поле последствий. Однако,
Существующее поведение не является питоническим, и Python является успешным, потому что очень мало в языке нарушает принцип наименьшего удивления где-либо около так сильно. Это реальная проблема, было бы разумно искоренить ее или нет. Это недостаток дизайна. Если вы понимаете язык намного лучше, пытаясь отследить поведение, я могу сказать, что C ++ делает все это и многое другое; вы многому научитесь, перемещаясь, например, по незаметным ошибкам указателя. Но это не Pythonic: люди, которые заботятся о Python достаточно, чтобы упорствовать перед лицом такого поведения, - это люди, которых привлекает этот язык, потому что в Python гораздо меньше сюрпризов, чем в другом языке. Любопытные и любопытные становятся питонистами, когда удивляются тому, как мало времени требуется, чтобы что-то заработало - не из-за дизайна, - я имею в виду,