kb84tkhrのブログ

何を書こうか考え中です あ、あと組織とは関係ないってやつです 個人的なやつ

Pythonのクラスで謎の現象

こんなクラス作ってですね

class Point:

    def __init__(self, x: float=0.0, y: float=0.0) -> None:
        self.x = x
        self.y = y

    def __repr__(self) -> str:
        return "Point({}, {})".format(self.x, self.y)


class Segment:

    def __init__(self, p1: Point = Point(), p2: Point = Point()) -> None:
        self.p1: Point = p1
        self.p2: Point = p2

    def __repr__(self) -> str:
        return "Segment({}, {})".format(self.p1, self.p2)

Segmentのオブジェクトをふたつ作って

$ python3 -i shape.py
>>> s1 = Segment()
>>> s2 = Segment()

片方を変更すると

>>> s1.p1.x = 1.0
>>> s1
Segment(Point(1.0, 0.0), Point(0.0, 0.0))

もう片方も変わってしまうという現象が!

>>> s2
Segment(Point(1.0, 0.0), Point(0.0, 0.0))

でもs1とs2が同じオブジェクトというわけではない

>>> print(s1 is s2)
False

s1.p1とs2.p1が同じオブジェクトだったという

>>> print(s1.p1 is s2.p1)
True

__init__Point()が1回しか呼ばれずに
__init__を呼ぶたびに使い回されてるってわけか

    def __init__(self, p1: Point = Point(), p2: Point = Point()) -> None:

そういえばEffective Pythonに書いてあった気がするわー
はまったわー
こういうことか

    def __init__(self, p1: Point = None, p2: Point = None) -> None:
        self.p1: Point = Point() if p1 is None else p1
        self.p2: Point = Point() if p2 is None else p2

どうだ

 python3 -i shape.py
>>> s1 = Segment()
>>> s2 = Segment()
>>> s1.p1.x = 1.0
>>> s1
Segment(Point(1.0, 0.0), Point(0.0, 0.0))
>>> s2
Segment(Point(0.0, 0.0), Point(0.0, 0.0))

おk