getattr 方法解析

Erstellt am: 18. Januar 2025

Beantwortet mit OpenAI 01 von OpenAI01

Frage

class Demo(object):
def run(self, name, age):
return f"My name is {name}, age is {age}!"


aa = getattr(Demo, 'run')
print(aa)

bb = getattr(Demo(), 'run')
print(bb)

cc = getattr(Demo, 'run')(1, "dgw", "26")
print(cc)

dd = getattr(Demo(), 'run')("dgw", "26")
print(dd)

分别是什么意思?

Gedanke

Analyzing function bindings

OK, let me see. The method run is bound to the Demo class, showing it's a function object but not tied to any instance. In Python 3, it's just a function, not an unbound method like in Python 2.

Examining method bindings

OK, let's break this down: bb is a bound method from the Demo class instance, with self set. Calling cc uses Demo.run with mismatched arguments, producing a functioning but incorrect output.

Analyzing bound and unbound methods

OK, let me see: bound methods effectively use self from the instance, while unbound methods are invoked without an instance, leading to different handling of arguments and output formatting.

Thought about Python getattr usage,持续 8 秒

Antwort

下面我们逐行分析下这段代码做了什么,以及打印出来的结果分别代表了哪些含义。示例代码如下(做了些简化):

class Demo(object): def run(self, name, age): return f"My name is {name}, age is {age}!" aa = getattr(Demo, 'run') print(aa) bb = getattr(Demo(), 'run') print(bb) cc = getattr(Demo, 'run')(1, "dgw", "26") print(cc) dd = getattr(Demo(), 'run')("dgw", "26") print(dd)

1. aa = getattr(Demo, 'run')

  • Demo.run 在 Python 3 中是一个「函数对象」(可以理解为没有绑定实例的函数),在 Python 2 里会显示为 unbound method,但在 Python 3 里仅仅是一个 function。
  • 因此 aa 相当于拿到了 Demo 类里定义的 run 这个函数本身,但它没有和任何具体的实例绑定
  • print(aa) 输出时,通常会看到类似:
    <function Demo.run at 0x7f1234567890>
    这说明它是一个函数对象。

2. bb = getattr(Demo(), 'run')

  • Demo() 先生成了一个 Demo 的实例。
  • getattr(Demo(), 'run') 则相当于从实例上获取 run 方法。
  • 在 Python 中,通过实例拿到的方法是已经绑定了 self 的方法,称为 bound method。
  • 因此 bb 会是一个「绑定到 Demo() 这个对象」的函数引用。
  • print(bb) 时会看到类似:
    <bound method Demo.run of <__main__.Demo object at 0x7f1234567890>>
    这说明它是绑定到某个具体对象的可调用方法。

3. cc = getattr(Demo, 'run')(1, "dgw", "26")

  • 首先 getattr(Demo, 'run') 和第 1 步一样,得到的仍然是类上的函数对象(没有绑定实例)。
  • 然后我们直接调用这个函数对象,并传入了三个参数 (1, "dgw", "26")
  • 在 Python 中,定义方法 def run(self, name, age): ... 的本质,是 run 函数的第一个参数要接收实例对象(即 self)。如果我们直接通过类拿到函数本身来调用,那么需要手动去提供 self
  • 也就是说,这里调用时的实际参数对应:
    • self = 1
    • name = "dgw"
    • age = "26"
  • 因为方法体里并没有用到 self 来操作属性(也没做其它类型检查),所以这个“1”作为 self 不会触发报错,依然可以正常返回字符串——只要里面不引用 self 的其他方法/属性就行。
  • 返回结果会是:
    "My name is dgw, age is 26!"
  • 这样看起来虽然“能跑”,但并不符合常规用法,一般不会手动把“self”当作一个普通值传进去。

4. dd = getattr(Demo(), 'run')("dgw", "26")

  • Demo() 生成一个 Demo 实例;getattr(Demo(), 'run') 相当于拿到这个实例已经绑定好的 run 方法(就跟上面 bb 一样)。
  • 因为它是一个 bound method,所以当我们调用它时,只需要传入除 self 以外的参数即可:
    • name = "dgw"
    • age = "26"
  • 最终得到结果:
    "My name is dgw, age is 26!"

小结

  1. aa = getattr(Demo, 'run')

    • 拿到的是类上的函数对象(未绑定实例),aa 只是一个函数引用。
  2. bb = getattr(Demo(), 'run')

    • 拿到的是绑定了某个 Demo 实例的 method 对象,也就是 bound method。
  3. cc = getattr(Demo, 'run')(1, "dgw", "26")

    • 直接调用类上的函数对象,并手动传递“self=1”,所以实参次序对应 (self, name, age)。虽然看起来怪异,但因为函数内部没用 self 做特殊操作,所以不会报错,返回相同的字符串。
  4. dd = getattr(Demo(), 'run')("dgw", "26")

    • 先从实例获得一个绑定方法,然后正常地只传入 nameage 就可以调用,返回"My name is dgw, age is 26!"

这样就能清晰区分为什么会有“unbound function(或function)” 和 “bound method” 的区别,以及传参为什么会不一样。

Diesen Q&A teilen