pythonmetaprogrammingpython-typing

How to set builtin class methods with python type function?


The following code works as expected

class Foo: pass
a1 = Foo
a2 = Foo
print(a1 == a2)
# True

now if I dynamically generate them and compare it it doesn't work

def eq(a,b):
  return str(a) == str(b)

t1 = type("Foo", (), {"__eq__": eq})
t2 = type("Foo", (), {"__eq__": eq})

print(t1 == t2)
# False

Moreover when running this, I get the error

print(t1.__eq__(t2))

Traceback (most recent call last):
  File "foo.py", line 51, in <module>
    print(t1.__eq__(t2))
TypeError: eq() missing 1 required positional argument: 'b'

What's the correct way to setup a builtin methods with using a type function ?

sidenote:
I need this kind of functionality, where I am dynamically creating the types either from types(in this case it's a identity function where I return output same as input) or type annotation which are not of type type but either typing.Alias or typing.Generic. Here is the snippet to get bigger picture

from typing import List
class Foo: pass

def build_type(type_):
  '''Build type definition of new user-defined types or annotations.'''

  if isinstance(type_, type):
    return type_
  else:
    origin = type_.__origin__ if hasattr(type_, "__origin__") else None
    args = type_.__args__ if hasattr(type_, "__args__") else None
    type_name = str(type_)
    attrs = {
      "__origin__": origin,
      "__args__": args,
      "__hash__": hash((type_name, origin, args))
    }
    return type(type_name, (), attrs)

t1 = build_type(Foo)
t2 = build_type(Foo)
print(t1 == t2) # True

a1 = build_type(List[Foo])
a2 = build_type(List[Foo])

print(a1 == a2) # False

Solution

  • As you were told in comments, methods have to be members of the class, not of the instance.

    As you are dynamically building classes and not simple objects, you should use a custom meta-class with a specific __eq__ method:

    You example would become:

    ...
    class Builder(type):
    """Custom meta-class defining type equality as name equality"""
        def __eq__(self, other):
            return str(self) == str(other)
    
    def build_type(type_):
      '''Build type definition of new user-defined types or annotations.'''
    
      if isinstance(type_, type):
        return type_
      else:
        origin = type_.__origin__ if hasattr(type_, "__origin__") else None
        args = type_.__args__ if hasattr(type_, "__args__") else None
        type_name = str(type_)
        attrs = {
          "__origin__": origin,
          "__args__": args,
          "__hash__": hash((type_name, origin, args))
        }
        return Builder(type_name, (), attrs)
    ...
    

    If you run it, you should get as expected:

    True
    True