Я действительно удивлен, что никто не выполнил проницательную интроспекцию, предлагаемую Python (2
и 3
применяются) на вызываемые объекты.
Учитывая небольшую небольшую функцию func
, определенную как:
>>> def func(a = []):
... a.append(5)
Когда Python сталкивается с ней, первое, что он сделает, это скомпилировать ее, чтобы создать code
объект для этой функции. Хотя этот шаг компиляции выполнен, Python оценивает *, а затем сохраняет аргументы по умолчанию (пустой список []
здесь) в самом объекте функции . Как упоминалось выше, список a
теперь можно считать членом функции func
.
Итак, давайте сделаем некоторые интроспекции, до и после изучите, как список расширяется внутри объекта функции. Я использую для этого Python 3.x
, для Python 2 это применимо (используйте __defaults__
или func_defaults
в Python 2, да, два имени для одной и той же вещи).
>>> def func(a = []):
... a.append(5)
...
После того, как Python выполнит это определение, он будет принимать любые заданные по умолчанию параметры (a = []
здесь) и вставить их в атрибут __defaults__
для объекта функции (соответствующий section: Callables):
>>> func.__defaults__
([],)
Хорошо, поэтому пустой список как единственная запись в __defaults__
, как и ожидалось.
Теперь приступим к выполнению этой функции:
>>> func()
Теперь давайте посмотрим на те __defaults__
еще раз:
>>> func.__defaults__
([5],)
Удивительно? Значение внутри объекта меняется! Последовательные вызовы функции теперь просто присоединяются к этому встроенному объекту list
:
>>> func(); func(); func()
>>> func.__defaults__
([5, 5, 5, 5],)
Итак, у вас есть причина, по которой этот «недостаток» потому что аргументы по умолчанию являются частью объекта функции. Здесь нет ничего странного. Все это немного удивительно.
Общее решение для борьбы с этим - обычное None
как значение по умолчанию, а затем инициализируется в теле функции:
def func(a = None):
# or: a = [] if a is None else a
if a is None:
a = []
Поскольку тело функции выполняется заново каждый раз, вы всегда получаете новый новый пустой список, если для a
не передан аргумент.
Чтобы еще раз убедиться, что список из __defaults__
совпадает с тем, что используется в функции func
, вы можете просто изменить свою функцию, чтобы вернуть id
списка a
, используемого внутри тела функции. Затем сравните его со списком в __defaults__
(позиция [0]
в __defaults__
), и вы увидите, как они действительно ссылаются на один и тот же экземпляр списка:
>>> def func(a = []):
... a.append(5)
... return id(a)
>>>
>>> id(func.__defaults__[0]) == func()
True
Все с сила самоанализа!
* Чтобы убедиться, что Python оценивает аргументы по умолчанию при компиляции функции, попробуйте выполнить следующее:
def bar(a=input('Did you just see me without calling the function?')):
pass # use raw_input in Py2
, как вы заметили, input()
вызывается перед процессом построения функции и связыванием ее с именем bar
.
Вы можете установить display
в flex
на почтовом контейнере и добавить новую опору, например, например. imageLeft
, который вы установили на true
для каждого другого поста и используете его для альтернативы flex-direction
между row
и row-reverse
для чередования положения изображения.
Пример
class Home extends React.Component {
state = {
posts: [
{ id: 0, title: "foo", imageURL: "https://placekitten.com/200/200" },
{ id: 1, title: "bar", imageURL: "https://placekitten.com/200/200" },
{ id: 2, title: "baz", imageURL: "https://placekitten.com/200/200" }
]
};
render() {
return (
<div>
<RenderPost posts={this.state.posts} />
</div>
);
}
}
function RenderPost(props) {
return (
<div>
{props.posts.map((post, index) => (
<PostPreview
key={post.id}
imageLeft={index % 2 === 0}
title={post.title}
image={post.imageURL}
/>
))}
</div>
);
}
function PostPreview({ image, imageLeft, title }) {
return (
<div
style={{
display: "flex",
width: 200,
flexDirection: imageLeft ? "row" : "row-reverse"
}}
>
<div
style={{
backgroundImage: `url(${image})`,
backgroundSize: "cover",
flexGrow: 1
}}
/>
<h1>{title}</h1>
</div>
);
}
ReactDOM.render(<Home />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>