'''
__new__ - вызывается для фактического создания нового объекта.
Он статический, т.к. экземпляра класса еще не существует
Метод __new__ не вызывает __init__.

class object:

    @staticmethod
    def __new__(cls, *more):
        pass
'''

class Point:
    def __new__(cls, *args, **kwargs):
        print('Point: Создание экземпляра')
        instance = object.__new__(cls)
        return instance
    
    def __init__(self, x, y):
        print('Point: Инициализация экземпляра')
        self.x = x
        self.y = y


def test_1():
    p = object.__new__(Point)
    p.__init__(10, 20)
    print(p)
    print(type(p))
    print(p.__dict__)


class Point3D(Point):
    def __new__(cls, *args, **kwargs):
        print('Point3D: Создание экземпляра')
        instance = super().__new__(cls) # вызываем через super
        return instance
    
    def __init__(self, x, y, z):
        print('Point3D: Инициализация экземпляра')
        super().__init__(x, y)
        self.z = z


def test_2():
    p = Point3D(10, 20, 30)
    print(p.__dict__)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

'''Создание атрибутов и методов в методе __new__'''

class SquareNumber(int):
    def __new__(cls, value):
        return super().__new__(cls, value ** 2)
    

def test_3():
    x = SquareNumber(3)
    print(x)
    print(isinstance(x, int))


class Square:
    def __new__(cls, w, h):
        # Реализация метода area
        cls.area = lambda self: self.width * self.height

        setattr(cls, 'perimeter', lambda self: 2 * (self.width + self.height))
        instance = super().__new__(cls)
        instance.width = w
        instance.height = h
        return instance
    
    def __init__(self, w, h):
        self.width = w
        self.height = h


def test_4():
    s = Square(3, 4)
    print(s)
    print(s.area())
    print(s.perimeter())


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'''Вызов __new__ в качестве статического метода
object.__new__(Square, 3, 4)
Square.__new__(Square, 3, 4)
'''

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'''Возвращаемое значение метода __new__'''

class Person:
    def __new__(cls, *args, **kwargs):
        print(f'Создание экземпляра {cls.__name__}')
        instance = super().__new__(cls)
        return instance
    
    def __init__(self):
        print(f'Инициализация экземпляра {self.__class__.__name__}')

def test_5():
    p = Person()        

'''
Здесь вызов __inti__ не произойдет, т.к. __new__ 
не возвращает экземпляр класаа
'''
class NewPerson:
    def __new__(cls, *args, **kwargs):
        print(f'Создание экземпляра {cls.__name__}')
        instance = 'Hellow world'
        return instance
    
    def __init__(self):
        print(f'Инициализация экземпляра {self.__class__.__name__}')

def test_6():
    p = NewPerson()        


class BestPerson:
    def __new__(cls, name, age):
        instance = super().__new__(cls)
        instance.name = name
        instance.age = age
        return instance
    
def test_7():
    p = BestPerson('Jackie Chan', 69)
    print(p.__dict__)



def main():
    # test_1()
    # test_2()
    # test_3()
    # test_4()
    # test_5()
    # test_6()
    test_7()

    

if __name__ == '__main__':
    main()