Ну, причина в том, что привязки выполняются при выполнении кода, и выполняется определение функции, ну ... когда функции определены.
Сравните это:
class BananaBunch:
bananas = []
def addBanana(self, banana):
self.bananas.append(banana)
Этот код страдает от такого же неожиданного случая. bananas - это атрибут класса, и, следовательно, когда вы добавляете к нему вещи, он добавляется ко всем экземплярам этого класса. Причина в том, что это точно то же самое.
Это просто «Как это работает», и заставить его работать по-другому в функциональном случае, вероятно, будет сложно, а в случае класса вероятно невозможно или, по крайней мере, замедлить объект создание экземпляра, так как вам придется хранить код класса и выполнять его при создании объектов.
Да, это неожиданно. Но как только пенни падает, она прекрасно вписывается в то, как работает Python в целом. На самом деле, это хорошее учебное пособие, и как только вы поймете, почему это происходит, вы будете намного лучше читать python.
Это говорит о том, что он должен занимать видное место в любом хорошем учебнике Python. Потому что, как вы говорите, все рано или поздно сталкиваются с этой проблемой.
Если вам разрешено передавать пустую C-строку, когда у вас нет значения для второго параметра, вы можете использовать вспомогательный функтор, который будет проверять arg2
и возвращать значение по умолчанию, если оно пустое. Что-то вроде этого:
#define DEFAULT_ARG "arg2"
struct helper_class {
char* operator()(char* arg)
{
if (*arg) return arg; else return DEFAULT_ARG;
}
} helper;
class func {
public:
func(int arg1 , char* arg2 = "arg2", int arg3 = 1) {}
};
int main()
{
func f1(42, helper(""), 9001); // default 2nd argument
func f2(42, helper("Its over 9000!"));
}
Не очень, я знаю ...
Прямо сейчас вы можете использовать std :: bind для выполнения этой операции или в c ++ 14/17 вы можете использовать лямбда-функцию и выполнить то же самое.
Своеобразное ограничение, что должен быть только один конструктор. Вот самое близкое, о котором я могу думать:
#include <iostream>
// cheap and cheerful Boost.Variant
struct StringOrInt {
char *s;
int i;
bool is_string;
StringOrInt(char *s) : s(s), i(0), is_string(true) {}
StringOrInt(int i) : s(0), i(i), is_string(false) {}
bool isInt() { return !is_string; }
int asInt() { return i; }
char *asString() { return s; }
};
struct Foo {
int m1;
char *m2;
int m3;
Foo(int arg1, StringOrInt arg2 = "arg2", int arg3 = 1) : m1(arg1) {
if (arg2.isInt()) {
arg3 = arg2.asInt();
arg2 = "arg2";
}
m2 = arg2.asString();
m3 = arg3;
}
void print() {
std::cout << m1 << " " << m2 << " " << m3 << "\n";
}
};
int main() {
Foo(1, "HelloWorld").print();
Foo(1, 2).print();
}
Обратите внимание, что с GCC это генерирует предупреждения, поскольку преобразование из строкового литерала в неконстантный char*
является устаревшим и неразумным. Но это то, о чем вы просили, и исправление его, чтобы параметры char*
и элемент данных были const char*
достаточно легкими.
Существенная слабость заключается в том, что это не мешает вам писать Foo(1,2,3)
, Чтобы проверить, что во время компиляции мне нужно несколько конструкторов. Чтобы проверить его во время выполнения, вы можете сделать третий параметр в другом классе, DefaultOrInt
, где Default
- это тип, используемый только для этой цели, поддерживающий только одно значение, используемое как значение по умолчанию для arg3
. Тогда, если arg2.isInt()
истинно, проверьте, что arg3.isInt()
является ложным и если не выбрасывает logic_error
.
Кроме того, нельзя использовать перегруженные конструкторы. Это должно быть сделано одним конструктором.
blockquote>Единственная причина, по которой я могу думать об этом требовании, состоит в том, что необязательные аргументы имеют один и тот же тип. В этом случае вы застряли, и вам захочется заглянуть в named constructor и / или с именем id .
В противном случае просто определите дополнительный конструктор. Это может привести к некоторому дублированию. значения по умолчанию.
Foo(int arg1 , char const *arg2 = "arg2", int arg3 = 1) { construct(arg1, arg2, arg3); } Foo(int arg1, int arg3) { construct(arg1, "arg2", arg3); }