0

print(I'm puzzled by what appears to be a bug in python3.

I want to create a class dynamically with some class methods. I seem to be able to do it like this:

import types

def class_body(ns):
    ns.update({"a": lambda self, x: x - 1})
    return ns

newc = types.new_class("foo", exec_body=class_body)

print(newc().a(3))

# prints 2 as expected

But I also want to create the class methods dynamically. I seem to be able to do something like:

import types
def funcs_gen(k=1):
    def fn(self, a):
        return a + k + self.i
    return fn

def class_body(ns):
    ns.update({"a": funcs_gen(k=2), "i": 5})
    return ns

newc = types.new_class("foo", exec_body=class_body)

print(newc().a(1))

# prints 8 as expected

But something weird seems to happen if I try to do the same with partial functions:

import types
from functools import partial

def fn(self, a, k=1):
    return a + k + self.i

def class_body(ns):
    ns.update({"a": partial(fn, k=2), "i": 5})
    return ns

newc = types.new_class("foo", exec_body=class_body)

print(newc().a(1))

# Unexpectedly produces: `TypeError: fn() missing 1 required positional argument: 'a'`
6
  • 1
    change def fn(self, a ... -> def fn(a ... Commented Oct 26, 2020 at 19:01
  • Sure, but in my real implementation I what if I want to access some class data. Commented Oct 26, 2020 at 19:03
  • or use partialmethod instead for class methods Commented Oct 26, 2020 at 19:03
  • 1
    partialmethod works as expected. Thanks! Commented Oct 26, 2020 at 19:08
  • 1
    Because partial objects do not implement the descriptor protocol to bind the instance as the first argument to itself. As an aside, note that types.new_class is a just a convienience function, you can also just use type directly, which takes type(name, bases, namespace) Commented Oct 26, 2020 at 19:20

1 Answer 1

1

Use partialmethod for methods.

import types
from functools import partialmethod

def fn(self, a, k=1):
    return a + k + self.i

def class_body(ns):
    ns.update({"a": partialmethod(fn, k=2), "i": 5})
    return ns

newc = types.new_class("foo", exec_body=class_body)

print(newc().a(1))

output:

8
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.