Search code examples
pythonplaywrightplaywright-python

Python Classes initialized by equal parameters do not equal


Im trying to debug my program, in which two classes, which are initialized by equal (note: == comparison not is) arguments. An example code + a snippet of the class + a object comparison is shown below.

Snippet Class Frame

    def __init__(self, frame: PlaywrightFrame, page: Page) -> None:
        super().__init__(frame)
        self._impl_obj = frame._impl_obj
        self._page = page
        self._frame = frame
        self._parent_frame = frame.parent_frame

        self._origin_query_selector = frame.query_selector
        frame.query_selector = self.query_selector
        self._origin_query_selector_all = frame.query_selector_all
        frame.query_selector_all = self.query_selector_all  # type: ignore
        self._origin_wait_for_selector = frame.wait_for_selector
        frame.wait_for_selector = self.wait_for_selector
        self._origin_add_script_tag = frame.add_script_tag
        frame.add_script_tag = self.add_script_tag
        self._origin_add_style_tag = frame.add_style_tag
        frame.add_style_tag = self.add_style_tag
        self._origin_frame_element = frame.frame_element
        frame.frame_element = self.frame_element

        self._origin_evaluate_handle = frame.evaluate_handle
        frame.evaluate_handle = self.evaluate_handle
        self._origin_wait_for_function = frame.wait_for_function
        frame.wait_for_function = self.wait_for_function
        self._origin_frame_locator = frame.frame_locator
        frame.frame_locator = self.frame_locator
        self._origin_locator = frame.locator
        frame.locator = self.locator
frame0, page0 = frame._frame, frame._page
frame1, page1 = owner_frame._frame, owner_frame._page
    
print(frame0, frame1, frame0 == frame1)
print(page0, page1, page0 == page1)
    
new_frame0 = Frame(frame0, page0)
new_frame1 = Frame(frame1, page1)
print(new_frame0, new_frame1, new_frame0 == new_frame1)


print(new_frame0.__dict__.items() ^ new_frame1.__dict__.items())
print(DeepDiff(new_frame0, new_frame1))

Output

<Frame name= url='...'> <Frame name= url='...'> True
<Page url='...'> <Page url='...'> True
<Frame name= url='...'> <Frame name= url='...'> False


{('_origin_wait_for_function', <bound method Frame.wait_for_function of <Frame name= url='...'>>), ('_origin_locator', <bound method Frame.locator of <Frame name= url='...'>>), ('_origin_query_selector', <bound method Frame.query_selector of <Frame name= url='...'>>), ('_origin_wait_for_selector', <bound method Frame.wait_for_selector of <Frame name= url='...'>>), ('_origin_query_selector_all', <bound method Frame.query_selector_all of <Frame name= url='...'>>), ('_origin_query_selector_all', <bound method Frame.query_selector_all of <Frame name= url='...'>>), ('_origin_evaluate_handle', <bound method Frame.evaluate_handle of <Frame name= url='...'>>), ('_origin_add_script_tag', <bound method Frame.add_script_tag of <Frame name= url='...'>>), ('_origin_frame_locator', <bound method Frame.frame_locator of <Frame name= url='...'>>), ('_origin_add_style_tag', <bound method Frame.add_style_tag of <Frame name= url='...'>>), ('_origin_evaluate_handle', <bound method Frame.evaluate_handle of <Frame name= url='...'>>), ('_origin_locator', <bound method Frame.locator of <Frame name= url='...'>>), ('_origin_add_style_tag', <bound method Frame.add_style_tag of <Frame name= url='...'>>), ('_origin_query_selector', <bound method Frame.query_selector of <Frame name= url='...'>>), ('_origin_add_script_tag', <bound method Frame.add_script_tag of <Frame name= url='...'>>), ('_origin_frame_element', <bound method Frame.frame_element of <Frame name= url='...'>>), ('_origin_frame_locator', <bound method Frame.frame_locator of <Frame name= url='...'>>), ('_origin_wait_for_function', <bound method Frame.wait_for_function of <Frame name= url='...'>>), ('_origin_frame_element', <bound method Frame.frame_element of <Frame name= url='...'>>), ('_origin_wait_for_selector', <bound method Frame.wait_for_selector of <Frame name= url='...'>>)}

{'unprocessed': ["root: <Frame name= url='...'> and <Frame name= url='...'>"]}

Solution

  • I would recommend overriding the __eq__ method of the class, and put in the member variables that you want to compare.

    Take the following snippet for example:

    class SomeClass:
        def __init__(self, a: int):
            self.a = a
    
    
    obj_1 = SomeClass(1)
    obj_2 = SomeClass(1)
    
    print(obj_1 == obj_2)
    

    You will see that comparison fails (Output is False) because by default == for class objects compares id, which will definitely be different.

    However, if you override the behaviour to compare the member variables that you want to compare, you can achieve what you desire:

    from __future__ import annotations
    
    
    class SomeClass:
        def __init__(self, a: int):
            self.a = a
    
        def __eq__(self, other: SomeClass):
            if not type(self) == type(other):
                return False
    
            return self.a == other.a
    
    
    obj_1 = SomeClass(1)
    obj_2 = SomeClass(1)
    obj_3 = SomeClass(3)
    
    print(obj_1 == obj_2)
    print(obj_1 == obj_3)
    

    The output of this is:

    True
    False
    

    This is because now it is comparing the two classes as you asked it to compare, and not just the id..

    Cheers!