gpt4 book ai didi

python - Python是如何将一个函数变成一个方法的?

转载 作者:行者123 更新时间:2023-12-03 22:28:50 28 4
gpt4 key购买 nike

我知道函数只是描述符,如下所示:

def func(self):
print(self.name)


class C:
def __init__(self, name):
self.name = name


C.func = func
c = C("foo")
c.func()
一开始我以为 c.func等于 C.func.__get__(c) ,是的, C.func.__get__(c)返回一个绑定(bind)的方法。但是当我设置 __get__函数为无, c.func仍然返回绑定(bind)方法。
def func(self):
print(self.name)


class C:
def __init__(self, name):
self.name = name


func.__get__ = None
C.func = func
c = C("foo")
c.func
输出:
<bound method func of <__main__.C object at 0x0000027EB23BF088>>
所以我很困惑。此外,我发现当从实例调用函数时,Python实际上调用了类的 ___getAttribute__。方法,它返回一个绑定(bind)的方法。
def func(self):
print(self.name)

func.__get__ = None

class C:
def __getattribute__(self, name):
r = super().__getattribute__(name)
print(r) # r is a bound method already
return r

def __init__(self, name):
self.name = name


C.func = func
c = C("foo")
c.func
输出:
<bound method func of <__main__.C object at 0x0000027EB243D1C8>>
func.__get__似乎没有任何效果。那么, __getattribute__ 中发生了什么? Python是如何将一个函数变成一个方法的?我已经用谷歌搜索并做了一些研究,但我仍然找不到答案。
也许我把事情弄复杂了,在我的理解中,函数本身就是一个描述符,但是就像下面的代码一样,我设置了 funcNone ,它可以正常工作:
class C:
def func(self):
print('hello world')

func.__get__ = None


c = C()
c.func()
但如果它是描述符,它将引发 TypeError :
class C:
class D:
def __get__(self, inst, cls):
if inst is None:
return self
return 'hello world'

D.__get__ = None

func = D()


c = C()
c.func

最佳答案

好吧,如果我从我的发现中正确理解的话。 (由于我不知道描述符,这正是我喜欢帮助的原因,还在学习中)
首先我们来看__getattr____getattribute__ .
让我们有一个空类A

class A:
pass
如果我初始化一个对象并尝试调用一个属性,因为目前没有,我们得到 AttributeError .
a = A()
a.some_property
发生以下情况:
Python, get attribute flow
简单的流量检查:
class FlowDemo:
def __init__(self):
self.inited_property = True


def __getattribute__(self, item):
if item in ('__class__', '__len__') : # For less spam of getting this attribute, if you want, you can remove condition.
print('Get Attribute', item)
# Call default behavior
return super().__getattribute__(item)


def __getattr__(self, item):
print('Get Attr', item)
if item == 'some_magic_name':
return "It's magic!"
raise AttributeError
fd = FlowDemo()

fd.inited_property
# Get Attribute inited_property
# True

fd.some_magic_property
# Get Attribute some_magic_name
# Get Attr some_magic_name
# "It's magic!"

fd.some_property
# Get Attribute some_property
# Get Attr some_property
# Traceback (most recent call last):
# File "<input>", line 1, in <module>
# File "stack-class-property-and-descriptors.py", line 67, in # __getattr__
# raise AttributeError
# AttributeError
这大概是可以理解的,包括使用。但可以肯定的是,我将举一个例子。此逻辑用作数据库结果的动态表示(将属性映射到普通字典、列表等)。
但它也可以只是访问属性(属性)的逻辑,例如访问计数器或验证(但这适用于 __setattr____setattribute__ )
那么描述符呢?
首先让我们看一下数据描述符,它们对我来说更容易理解。
这是具有 __get__ 的类或解码器和 __set__ 中的一个或两个和 __delete__ .
一旦定义了这个,python,当在属性定义中使用它时,它不会返回一个类,而是它通过 __get__获得的值。 , 在声明值时不会覆盖已声明的类,而是使用它的 __set__ .
例子:
class WeekDayDescriptor:
def __init__(self):
self.__week_day = 0


def __get__(self, instance, owner=None):
return self.__week_day


def __set__(self, instance, value):
if not isinstance(value, int):
raise TypeError('Value must be int')
if not (0 <= value < 6):
raise ValueError('Value must be in range 0 - 6')
self.__week_day = value



class Calendar:
week_day = WeekDayDescriptor()

def __init__(self, week_day):
self.week_day = week_day

演示:
c = Calendar(9)
# ValueError: Value must be in range 0-6

c = Calendar('6')
# TypeError: Value must be int

c = Calendar(3)
c.week_day = 6

c.week_day = 10
# ValueError: Value must be in range 0-6

c.week_day = 'monday'
# TypeError: Value must be int
装饰器描述符:
class Calendar:
@property
def week_day(self):
return self.__week_day

@week_day.setter
def week_day(self, week_day):
if not isinstance(week_day, int):
raise TypeError('Value must be int')
if not (0 <= week_day < 6):
raise ValueError('Value must be in range 0 - 6')
self.__week_day = week_day

def __init__(self, week_day):
self.week_day = week_day
pass
现在对于非数据描述符......
非数据描述符是只有 __get__ 的描述符。 .
据我了解,每个方法都会自动拥有自己的描述符,这要归功于函数获得对对象的引用 - self .
我们可以为函数/方法编写自己的描述符,但这并不是那么简单,我们必须帮助自己并绕过它。
def function_as_method(self, value):
print(self, value)

class HelperDescriptor:
def __get__(self, instance, owner):
def wrapper(*args, **kwargs):
return function_as_method(instance, *args, **kwargs)
return wrapper

class Foo:
baz = HelperDescriptor()

>>> bar = Foo()
>>> bar.baz(1)
<__main__.Foo object at 0x7f64f7768b70> 1
Source of last code block, but in czech lang.

最后,当我们设置 __get__ 时,您提到的问题至 None并且您仍然可以获得对该函数的引用。
很简单,python没有直接区分原始数据类型和函数,都是有值的变量(或属性/属性)。无论是值(value)还是可调用的都是另一回事。
def f(): return True
print(type(f), f())
# <class 'function'> True

f = 123
print(type(f), f)
# <class 'int'> 123
因此,当我们要求 obj.func方法或调用它 obj.func()直接,前两个变魔法先调用- __getattribute____getattr__ .
如果我们调用一个方法,它只有在我们获得对内存中函数的引用后才会被调用。
再举一个简单的例子:
def func(self, value):
print('Printing:', value)


class PrintDescriptor:
def __init__(self, name):
self.name = name

def __get__(self, instance, owner):
def wrapper(*args, **kwargs):
print(f"Calling '{self.name}' method")
return func(instance, *args, **kwargs)
return wrapper


class B:
foo = PrintDescriptor('foo')
bar = PrintDescriptor('bar')

def __getattribute__(self, item):
if item not in ('__len__', '__class__', '__dict__'):
print('Get Attribute', item)

return super().__getattribute__(item)
演示:
b = B()

b.foo
# Get Attribute foo
# <function PrintDescriptor.__get__.<locals>.wrapper at 0x7f774a782ee0>

b.foo(2)
# Get Attribute foo
# Calling 'foo' method
# Printing: 2

b.bar(4)
# Get Attribute bar
# Calling 'bar' method
# Printing: 4
资料来源:
  • https://www.datacamp.com/community/tutorials/python-descriptors#above1
  • https://blog.milde.cz/post/319-pokrocile-techniky-v-pythonu-deskriptory/
  • Python Doc, __get__
  • Python Docs, __getattribute__
  • Python Docs, __getattr__
  • 关于python - Python是如何将一个函数变成一个方法的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66668746/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com