Я думаю, что ответ на этот вопрос заключается в том, как python передает данные в параметр (передать по значению или по ссылке), а не изменчивость или как питон обрабатывает инструкцию «def».
Краткое введение. Во-первых, в питоне есть два типа типов данных: один простой элементарный тип данных, например числа, а другой тип данных - объекты. Во-вторых, при передаче данных в параметры python передает элементарный тип данных по значению, т. Е. Делает локальную копию значения локальной переменной, но передает объект по ссылке, т. Е. Указывает на объект.
Признавая вышеуказанные два момента, давайте объясним, что произошло с кодом python. Это происходит только из-за передачи по ссылке для объектов, но не имеет ничего общего с mutable / immutable или, возможно, факта, что оператор «def» выполняется только один раз, когда он определен.
[] является объектом , поэтому python передает ссылку [] на a
, т. е. a
является только указателем на [], который лежит в памяти как объект. Существует только одна копия [] с, однако, многими ссылками на нее. Для первого foo () список [] изменен на 1 методом добавления. Но учтите, что существует только одна копия объекта списка, и этот объект теперь становится 1 . При запуске второго foo (), какая веб-страница effbot говорит (элементы больше не оцениваются) неверна. a
оценивается как объект списка, хотя теперь содержимое объекта 1 . Это эффект прохождения по ссылке! Результат foo (3) может быть легко получен таким же образом.
Чтобы еще раз подтвердить мой ответ, давайте рассмотрим два дополнительных кода.
===== = № 2 ========
def foo(x, items=None):
if items is None:
items = []
items.append(x)
return items
foo(1) #return [1]
foo(2) #return [2]
foo(3) #return [3]
[]
- это объект, поэтому None
(первый является изменяемым, в то время как последний является неизменным, но изменчивость не имеет ничего что касается вопроса). Никто не находится где-то в пространстве, но мы знаем, что он есть, и там есть только одна копия «Нет». Таким образом, каждый раз, когда вызывается foo, элементы оцениваются (в отличие от некоторого ответа, который оценивается только один раз) равны None, чтобы быть ясным, ссылка (или адрес) None. Затем в foo элемент изменяется на [], т. Е. Указывает на другой объект, который имеет другой адрес.
====== № 3 =======
def foo(x, items=[]):
items.append(x)
return items
foo(1) # returns [1]
foo(2,[]) # returns [2]
foo(3) # returns [1,3]
Вызов элемента foo (1) указывает на объект списка [] с адрес, скажем, 11111111. содержимое списка будет изменено на 1 в функции foo в дальнейшем, но адрес не изменится, еще 11111111. Тогда foo (2, []) является приходит. Хотя [] в foo (2, []) имеет тот же контент, что и параметр по умолчанию [] при вызове foo (1), их адрес отличается! Поскольку мы предоставляем параметр явно, items
должен принять адрес этого нового []
, скажем 2222222, и вернуть его после внесения некоторых изменений. Выполняется foo (3). поскольку предоставляется только x
, элементы должны снова принимать значение по умолчанию. Что такое значение по умолчанию? Он задается при определении функции foo: объект списка, расположенный в 11111111. Таким образом, элементы оцениваются как адрес 11111111, имеющий элемент 1. Список, расположенный в 2222222, также содержит один элемент 2, но он не указывается элементами any Больше. Следовательно, добавление 3 сделает items
[1,3].
Из приведенных выше объяснений видно, что веб-страница effbot , рекомендованная в принятом ответе, не дала соответствующего ответа на этот вопрос. Более того, я думаю, что точка на веб-странице effbot неверна. Я думаю, что код UI.Button верен:
for i in range(10):
def callback():
print "clicked button", i
UI.Button("button %s" % i, callback)
Каждая кнопка может содержать определенную функцию обратного вызова, которая будет отображать различное значение i
. Я могу привести пример, чтобы показать это:
x=[]
for i in range(10):
def callback():
print(i)
x.append(callback)
Если мы выполним x[7]()
, мы получим 7, как ожидалось, и x[9]()
даст 9, другое значение i
.
Такие конструкции следует избегать, когда это возможно. Поэтому спросите себя: действительно ли этот параметр необходим как аргумент конструктора? Или может SomeObject быть заменен на безгосударственный, который повторно используется всеми, кто зависит от него, передавая параметр методу, который вы выполняете на объекте?
, например. Вместо
public class SomeObject
{
private float someValue
public SomeObject(IService service, float someValue)
{
this.someValue = someValue
}
public float Do(float x)
{
return this.Service.Get(this.someValue) * x;
}
}
используйте
public class SomeObject
{
public SomeObject(IService service)
{
}
public float Do(float x, float someValue)
{
return this.Service.Get(someValue) * x;
}
}
Если требуется, пошли за фабрикой:
public interface ISomeObjectFactory
{
ISomeObject CreateSomeObject(float someValue);
}
public class SomeObjectFactory : ISomeObjectFactory
{
private IKernel kernel;
public SomeObjectFactory(IKernel kernel)
{
this.Kernel = kernel;
}
public ISomeObject Create(float someValue)
{
return this.kernel.Get<ISomeObject>(WithConstructorArgument("someValue", someValue);
}
}
Preview: Ninject 2,4 won 't требуется реализация, но разрешить
kernel.Bind<ISomeObjectFactory>().ToFactory(); // or maybe .AsFactory();
Другой подход - инициализация в два этапа (не связанная с ninject, любая структура DI):
public class SomeObject
{
private readonly IService _service;
public SomeObject(IService service)
{
// constructor only captures dependencies
_service = service;
}
public SomeObject Load(float someValue)
{
// real initialization goes here
// ....
// you can make this method return no value
// but this makes it more convienient to use
return this;
}
}
и использование:
public static class TestClass
{
public static void TestMethod(IService service)
{
//var someObject = new SomeObject(service, 5f);
var someObject = new SomeObject(service).Load(5f);
}
}
Вы действительно не должны пытаться использовать D.I. для этого. Вы можете придумать всевозможные дурацкие решения, но они могут не иметь смысла в будущем.
Наш подход заключается в создании фабрики через D.I., и тогда метод Create Factory будет построен с использованием переданного в D.I. контейнер. Нам не нужно часто использовать этот шаблон, но когда мы это делаем, он делает продукт намного чище (поскольку он делает наши графики зависимостей меньше).
В NInject, с которым вы отметили это, вы вводите автоматически созданный Factory в форме Func<parameters you wish to feed in,T>
, используя FuncModule, как описано в этом сообщении .
Этот подход также доступен в autofac для одного.
Различные ответы Factory method рассматриваются в ответах на этот вопрос .
EDIT: NB Хотя это может быть интересно, воспользуйтесь решением @Remo Gloor (и критически советуем избегать такого решения)
Я бы, вероятно, использовал наивное решение этого. Если вам известно значение someValue
, когда оно вам нужно, я бы удалил его из конструктора и добавил свойство к вашему объекту, чтобы вы могли установить someValue
. Таким образом вы можете получить свой объект из своего контейнера, а затем установить значение, когда у вас есть объект.
Мое другое предложение состоит в том, что вы вместо прямого доступа к нему создаете фабрику, которую вы можете использовать для создания такого объекта. Затем вы регистрируете завод в своем контейнере и используете фабрику для создания своего экземпляра. Что-то вроде этого:
public class SomeObjectFactory : ISomeObjectFactory
{
private IYourService _service;
public SomeObjectFactory(IYourService service)
{
_service = service;
}
public ISomeObject Create(float someValue)
{
return new SomeObject(_service, someValue);
}
}
вы можете попробовать такой шаблон.
UPDATE: Обновлен код, чтобы отразить комментарии к улучшению.
SomeObjectFactory
. Это не рекомендуется. Вместо этого вставьте экземпляр IYourService
в конструктор SomeObjectFactory
и пусть conatiner разрешит эту зависимость в корне композиции.
– Johann Gerell
4 August 2011 в 10:08
ToFactory
собирается делать. Поэтому забудьте о том, что я сказал, и спасибо за объяснение! И это su_gg_est: P Кстати, вы ошибочно указали имя метода как Create (not CreateSomeObject). Я лично буду использовать Func в предпочтении, поскольку я живу в запутанном мире ... – Ruben Bartelink 5 August 2011 в 00:30