Просто хочу добавить слово о намерении (в отличие от поведения) определения __new__
против __init__
.
Я столкнулся с этим вопросом (среди прочего), когда пытался понять, как лучше определить фабрику классов. Я понял, что одним из способов, которыми __new__
концептуально отличается от __init__
, является тот факт, что выгода __new__
- это именно то, что было указано в вопросе:
Таким образом, единственным преимуществом метода __new__ является то, что переменная экземпляра будет начинаться как пустая строка, а не как NULL. Но почему это всегда полезно, так как если бы мы заботились о том, чтобы переменные нашего экземпляра инициализировались к какому-либо значению по умолчанию, мы могли бы просто сделать это в методе __init__?
Учитывая заявленный сценарий, мы заботимся о начальных значениях переменных экземпляра, когда instance на самом деле сам является классом. Итак, если мы динамически создаем объект класса во время выполнения, и нам нужно определить / контролировать что-то особенное в последующих экземплярах этого класса, мы определяем эти условия / свойства в методе __new__
метакласса.
Я был смущен этим, пока не подумал о применении концепции, а не о ее значении. Вот пример, который, надеюсь, прояснит разницу:
a = Shape(sides=3, base=2, height=12)
b = Shape(sides=4, length=2)
print(a.area())
print(b.area())
# I want `a` and `b` to be an instances of either of 'Square' or 'Triangle'
# depending on number of sides and also the `.area()` method to do the right
# thing. How do I do that without creating a Shape class with all the
# methods having a bunch of `if`s ? Here is one possibility
class Shape:
def __new__(cls, sides, *args, **kwargs):
if sides == 3:
return Triangle(*args, **kwargs)
else:
return Square(*args, **kwargs)
class Triangle:
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return (self.base * self.height) / 2
class Square:
def __init__(self, length):
self.length = length
def area(self):
return self.length*self.length
Обратите внимание, что это только наглядный пример. Существует несколько способов получить решение, не прибегая к подходу фабрики классов, как описано выше, и даже если мы решим реализовать решение таким образом, для краткости остаются некоторые оговорки (например, явное объявление метакласса). )
Если вы создаете обычный класс (он же неметакласс), тогда __new__
не имеет смысла, если только он не является особым случаем, подобным изменчивому сценарию против неизменяемого в ответе ncoghlan ответ по сути, это более конкретный пример концепции определения начальных значений / свойств класса / типа, создаваемого с помощью __new__
, который затем инициализируется с помощью __init__
).