Python условно инициализирует подкласс от одного из двух родителей [duplicate]

TL; DR: Попробуйте использовать Html.Partial вместо Renderpage


Я получал Object reference not set to an instance of an object, когда пытался сделать вид в представлении, отправив ему модель, например это:

@{
    MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null

Отладка показала, что модель была Null внутри MyOtherView. Пока я не сменил его на:

@{
    MyEntity M = new MyEntity();
}
@Html.Partial("_MyOtherView.cshtml", M);

И это сработало.

Кроме того, причина, по которой я не имел Html.Partial для начала, заключалась в том, что Visual Studio иногда выдает ошибки, (f9), если он находится внутри другого построенного цикла foreach, хотя это не ошибка:

@inherits System.Web.Mvc.WebViewPage
@{
    ViewBag.Title = "Entity Index";
    List<MyEntity> MyEntities = new List<MyEntity>();
    MyEntities.Add(new MyEntity());
    MyEntities.Add(new MyEntity());
    MyEntities.Add(new MyEntity());
}
<div>
    @{
        foreach(var M in MyEntities)
        {
            // Squiggly lines below. Hovering says: cannot convert method group 'partial' to non-delegate type Object, did you intend to envoke the Method?
            @Html.Partial("MyOtherView.cshtml");
        }
    }
</div>

Но я смог запустить приложение без проблем с этим " ошибка". Я смог избавиться от ошибки, изменив структуру цикла foreach, чтобы выглядеть так:

@foreach(var M in MyEntities){
    ...
}

Хотя я чувствую, что это потому, что Visual Studio неправильно интерпретировала амперсанды и скобки .

14
задан sneak 9 July 2010 в 02:25
поделиться

3 ответа

Для этого вам нужно __new__(). (И вам также нужно сделать его классом нового стиля, предполагая, что вы используете Python 2, путем подкласса object.)

class ClassA(object):
    def __new__(cls,theirnumber):
        if theirnumber > 10:
            # all big numbers should be ClassB objects:
            return ClassB.ClassB(theirnumber)
        else:
            # numbers under 10 are ok in ClassA.
            return super(ClassA, cls).__new__(cls, theirnumber)

__new__() выполняется как часть процесса создания класса до __init__(). В основном __new__() - это то, что на самом деле создает новый экземпляр, а затем __init__() вызывается для инициализации его свойств. Вот почему вы можете использовать __new__(), но не __init__(), чтобы изменить тип созданного объекта: после запуска __init__() объект уже создан, и слишком поздно менять его тип. (Ну ... не совсем, но это попадает в очень загадочную черную магию Python.) См. документацию .

В этом случае, однако, я бы сказал, что фабричная функция более вероятно, что-то вроде

def thingy(theirnumber):
    if theirnumber > 10:
        return ClassB.ClassB(theirnumber)
    else:
        return ClassA.ClassA(theirnumber)

. Кстати, обратите внимание, что если вы сделаете то, что я сделал с __new__() выше, если возвращен ClassB, метод __init__() в ClassB будет вызываться not ! Python только вызывает __init__(), если объект, возвращенный из __new__(), является экземпляром класса, в котором содержится метод __new__() (здесь ClassA). Это еще один аргумент в пользу подхода фабричной функции.

30
ответ дан David Z 23 August 2018 в 22:29
поделиться
  • 1
    Хорошо, теперь, если я хочу поменять объект на новый внутри метода объекта, а не в конструкторе? Я хочу, чтобы объект, который превращается в другие объекты в течение своего жизненного цикла. – sneak 9 July 2010 в 03:14
  • 2
    @sneak, вы не можете: метод получает self как аргумент, и поэтому, что бы они ни переписывали в указанное имя, он не будет иметь никакого эффекта вне метода. (Вы можете, конечно, переустановить квалифицированные имена, такие как self.__class__ - квалифицированные имена и имена файлов полностью различаются почти во всех отношениях, о которых вы можете думать). – Alex Martelli 9 July 2010 в 03:27
  • 3
    @Alex, то, что вы можете назначить self.__class__, является ошибкой реализации в CPython; официальная спецификация python говорит, что это не должно работать. Даже если это не случайность, это довольно ужасно. Короче: пожалуйста, не делайте этого, и особенно, пожалуйста, не предлагайте это другим людям. – habnabit 9 July 2010 в 05:52
  • 4
    @Aaron, пожалуйста, укажите мне «официальную спецификацию python». что это не должно работать - я думаю, что вы ошибаетесь, как единственное упоминание, которое я вижу в языковых спецификациях, в docs.python.org/reference/datamodel.html#slots , говорит __class__ assignment works only if both classes have the same __slots__ (и поэтому, в частности, гарантируется работа, если ни один из классов не имеет __slots__). Насколько я думаю, вы ошибочны в том, что вы заявляете как факт о том, что «это не должно работать», я думаю, что вы не в своем рокере, говоря «это довольно ужасная вещь»: это прекрасный метод, когда это необходимо (не часто). – Alex Martelli 9 July 2010 в 06:12
  • 5
    @Alex, docs.python.org/reference/datamodel.html#id1 Я вспомнил это предложение, хотя и не сноску. В любом случае, сноска, похоже, согласна со мной в этом. – habnabit 9 July 2010 в 06:20

Не пытайтесь извратить назначение конструкторов: используйте заводскую функцию. Вызов конструктора для одного класса и возврат экземпляра другого класса - верный способ вызвать путаницу.

11
ответ дан Ned Batchelder 23 August 2018 в 22:29
поделиться
  • 1
    Думаю, мне удастся остаться без конфискации, тх. – sneak 9 July 2010 в 03:13

Я бы предложил использовать для этого шаблон фабрики. Например:

def get_my_inst(the_number):
   if the_number > 10:
       return ClassB(the_number)
   else:
       return ClassA(the_number)

class_b_inst = get_my_inst(500)
class_a_inst = get_my_inst(5)
13
ответ дан Sam Dolan 23 August 2018 в 22:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: