为了账号安全,请及时绑定邮箱和手机立即绑定

为什么绑定类实例方法与绑定类方法不同?

为什么绑定类实例方法与绑定类方法不同?

素胚勾勒不出你 2023-07-05 10:26:38
我正在阅读python 文档并偶然发现了以下几行:还需要注意的是,作为类实例属性的用户定义函数不会转换为绑定方法;仅当函数是类的属性时才会发生这种情况。请有人用简单的英语解释一下这是什么意思。我将介绍一些简写符号:让“用户定义函数”用f表示,让“类实例”用ci表示,而类则简单地用c表示。显然(?),ci = c(),有一些滥用符号。另外,允许用简单的集合表示法重铸成员资格语句,例如“作为类实例的属性的用户定义函数”简写为“ vf: fεa(ci) ”,其中v : ' for all ' 和 where ' a '是(一组)属性(例如,类或类实例)的简写,“ε”表示集合隶属函数。此外,绑定函数的过程由 ci.f(*args) 或 cf(*args) => f(ci, *args) 或 f(c, *args) 速记描述(前者指的是实例)方法调用,后面指的是类方法调用)使用新引入的速记符号,文档中的引用是否意味着vf: fεa(c), cf(*args) => f(c, *args) 是一个真实的陈述尽管vf: fεa(ci), ci.f(*args) => f(ci, *args) 为假?
查看完整描述

5 回答

?
开满天机

TA贡献1786条经验 获得超12个赞

将用户定义的方法设置为类的属性,这是错误的方式


考虑以下示例类A和函数f:



class A:

    pass


def f(self):

    print("I\'m in user-defined function")


a = A()


该函数f是单独定义的,不在类内部。


假设您想添加函数f作为a对象的实例方法。


f通过设置为属性来添加它a是行不通的:


import types


class A:

    pass


def f(self):

    print("I\'m in user-defined function")


a = A()

a.f = f


# <function f at 0x000002D81F0DED30>

print(a.f)


# TypeError: f() missing 1 required positional argument: 'self'

# a.f()

因为函数f没有绑定到对象a。


这就是为什么在调用a.f()它时会引发有关缺少参数的错误(如果f已绑定到a,则该对象a就是缺少参数self)。


这部分是文档提到的内容:


还需要注意的是,作为类实例属性的用户定义函数不会转换为绑定方法。


f当然,如果 function已在 class 内定义,则所有这一切都不会发生A,这就是文档中的以下部分所述:


...只有当函数是类的属性时才会发生这种情况。


将用户定义的方法设置为类的属性,正确的方法


要将函数添加f到对象,a您应该使用:


import types


class A:

    pass


def f(self):

    print("I\'m in user-defined function")


a = A()


a.f = types.MethodType( f, a )


# <bound method f of <__main__.A object at 0x000001EDE4768E20>>

print(a.f)


# Works! I'm in user-defined function

a.f()

它将用户定义的方法绑定f到实例a。


查看完整回答
反对 回复 2023-07-05
?
慕沐林林

TA贡献2016条经验 获得超9个赞

当您以通常的方式创建一个方法时,它将是一个绑定方法:它接收实例作为第一个参数(我们通常将其分配给“self”):


class A:

    def meth(*args):

        print(args)

        

        

a = A()

a.meth()

        

# (<__main__.A object at 0x7f56a137fd60>,)  

如果您采用一个普通函数并将其添加到类属性中,它将以相同的方式工作:


def f(*args):

    print(args)

    

A.f = f

a = A()

a.f()

# (<__main__.A object at 0x7f56a137f700>,)

实例作为第一个参数传递,它是一个绑定方法。


另一方面,如果您将函数作为类实例的属性,则它将不是绑定方法=调用时不会将实例作为第一个参数传递:


a = A()

a.f = f

a.f()

# ()  


查看完整回答
反对 回复 2023-07-05
?
哔哔one

TA贡献1854条经验 获得超8个赞

我不认为花哨的形式逻辑符号在这里有帮助。


然而,要回答这个问题:“作为类实例的属性的用户定义函数不会转换为绑定方法;只有当函数是类的属性时才会发生这种情况”是什么意思?


绑定方法是依赖于类的实例作为第一个参数的方法。它将实例作为第一个参数传递,用于访问变量和函数。在 Python 3 和更高版本的 python 中,类中的所有函数默认都是绑定方法。


因此,如果您创建用户定义函数作为类实例的属性,它不会自动转换为绑定方法。“类实例”只是 Python 中表达“对象”或“对象实例”在其他语言中含义的一种方式。


例如:


class HelloClass:

    greeting = 'Hello'


    def greet(self, name):

        print(f'{greeting} {name}')



hc = HelloClass()

hc.greet('John')

这HelloClass是类,而hc是类实例。greet是一个绑定方法,至少需要一个参数(按照约定调用),该参数在调用时自动分配给类实例 - 即打印之前self的值是类实例。selfhello Johnhc


现在,如果你尝试这样做:


def greet_with_hi(self, name):

    print(f'Hi {name}')



class HiClass:

    greet = greet_with_hi



hc = HiClass()

hc.greet('John')

这可行(尽管你的 IDE 可能会反对),但这根本不起作用:


def greet_with_hi(self, name):

    print(f'Hi {name}')



class HiClass:

    def __init__(self):

        self.greet = greet_with_hi



hc = HiClass()

hc.greet('John')

它导致TypeError: greet_with_hi() missing 1 required positional argument: 'name'. 它应该,因为.greet在 的实例上HiClass不是绑定方法,并且self greet_with_hi期望不会自动填充。


查看完整回答
反对 回复 2023-07-05
?
米脂

TA贡献1836条经验 获得超3个赞

我认为最好通过例子来阐明其含义。

假设我们有一个包含各种属性的类实例,这些属性是用户定义的函数。

  • add1通过将其定义为类定义的一部分来添加。

  • add2在实例化之前通过对类进行猴子修补来添加。

  • add3通过在实例化后对类进行猴子修补来添加

  • add4通过在实例化后对实例进行猴子修补来添加

class Number:


    def __init__(self, x):

        self.x = x


    def add1(self):

        return self.x + 1


def add2(self):

    return self.x + 2


def add3(self):

    return self.x + 3


def add4(self):

    return self.x + 4


setattr(Number, 'add2', add2)


two = Number(2)


setattr(Number, 'add3', add3)


setattr(two, 'add4', add4)


print(two.add1())  # prints 3

print(two.add2())  # prints 4

print(two.add3())  # prints 5

print(two.add4())  # TypeError: add4() missing 1 required positional argument: 'self'

我们尝试调用这些。


前三个都有效(在add3实例化时它不是类的属性甚至没有关系)。


请注意,当我们调用这些函数时,我们不会显式传递与函数内第一个位置参数相对应的任何内容(即self)——它会自动为我们添加。这就是绑定方法的含义。它是用一个位置参数声明的,而我们明确不传递任何位置参数。


但在这种情况下,add4它会抱怨缺少位置参数 - 这是因为该实例不会自动添加为第一个参数。(如果你明确使用它就会起作用two.add4(two)。)


查看完整回答
反对 回复 2023-07-05
?
Helenr

TA贡献1780条经验 获得超3个赞

绑定方法 python 绑定方法是依赖于类的实例作为第一个参数的方法。它将实例作为第一个参数传递,用于访问变量和函数。在 Python 3 和更高版本的 python 中,类中的所有函数默认都是绑定方法。


让我们通过一个例子来理解这个概念:


# Python code to demonstrate 

# use of bound methods 

  

class A: 

  

    def func(self, arg): 

        self.arg = arg 

        print("Value of arg = ", arg) 

  

  

# Creating an instance 

obj = A()   

  

# bound method 

print(obj.func) 

Output:


< bound method A.func of <__main__.A object at 0x7fb81c5a09e8>>

这里,


obj.func(arg) 由 python 翻译为 A.func(obj, arg)。实例 obj 自动作为第一个参数传递给被调用的函数,因此函数的第一个参数将用于访问对象的变量/函数。


让我们看一下 Bound 方法的另一个示例。


# Python code to demonstrate 

# use of bound methods 

  

class Car: 

    # Car class created 

    gears = 5

  

    # a class method to change the number of gears  

    @classmethod

    def change_gears(cls, gears): 

        cls.gears = gears 

  

  

# instance of class Car created 

Car1 = Car() 

  

  

print("Car1 gears before calling change_gears() = ", Car1.gears) 

Car1.change_gears(6)  

print("Gears after calling change_gears() = ", Car1.gears) 

  

# bound method 

print(Car1.change_gears) 

Output:


Car1 gears before calling change_gears() =  5

Gears after calling change_gears() =  6

<bound method Car.change_gears of <class '__main__.Car'>>

上面的代码是类方法的示例。类方法类似于绑定方法,只不过实例的类作为参数而不是实例本身传递。在上面的示例中,当我们调用 Car1.change_gears(6) 时,类“Car”作为第一个参数传递。


对这些绑定方法的需求 类内的方法至少需要一个参数。为了使它们成为零参数方法,必须使用“装饰器”。类的不同实例具有与其关联的不同值。


例如,如果有一个类“Fruits”,那么像苹果、橙子、芒果这样的实例都是可能的。每个实例可能有不同的大小、颜色、味道和营养成分。因此,要更改特定实例的任何值,该方法必须将“self”作为参数,以允许它仅更改其属性。


例子:


class sample(object): 

  

    # Static variable for object number 

    objectNo = 0

  

    def __init__(self, name1): 

  

        # variable to hold name 

        self.name = name1 

  

        # Increment static variable for each object 

        sample.objectNo = sample.objectNo + 1

  

        # each object's unique number that can be 

        # considered as ID 

        self.objNumber = sample.objectNo 

  

    def myFunc(self): 

        print("My name is ", self.name,  

              "from object ", self.objNumber) 

  

    def alterIt(self, newName): 

        self.name = newName 

  

    def myFunc2(): 

        print("I am not a bound method !!!") 

  

  

# creating first instance of class sample         

samp1 = sample("A") 

samp1.myFunc() 

  

# unhide the line below to see the error 

# samp1.myFunc2() #----------> error line 

  

  

# creating second instance of class sample     

samp2 = sample("B") 

samp2.myFunc() 

samp2.alterIt("C") 

samp2.myFunc() 

samp1.myFunc() 

Output:


My name is  A from object  1

My name is  B from object  2

My name is  C from object  2

My name is  A from object  1

在上面的示例中,创建了两个实例,即 samp1 和 samp2。请注意,当函数 alterIt() 应用于第二个实例时,仅更改该特定实例的值。samp1.myFunc() 行将扩展为sample.myFunc(samp1)。对于此方法,不需要传递显式参数。实例 samp1 将作为参数传递给 myFunc()。samp1.myFunc2() 行将生成错误:


Traceback (most recent call last):

  File "/home/4f130d34a1a72402e0d26bab554c2cf6.py", line 26, in 

    samp1.myFunc2() #----------> error line

TypeError: myFunc2() takes 0 positional arguments but 1 was given

这意味着该方法是未绑定的。它不接受任何实例作为参数。这些函数是未绑定函数。


查看完整回答
反对 回复 2023-07-05
  • 5 回答
  • 0 关注
  • 164 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信