鱼C论坛

 找回密码
 立即注册
查看: 581|回复: 13

[已解决]疑问

[复制链接]
发表于 2024-4-11 20:52:29 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
class Function:
     ...
     def __get__(self, obj, objtype=None):
         '''Simulate func_descr_get() in Objects/funcobject.c'''
         if obj is None:
             return self
         return MethodType(self, obj)

怎么确定obj是否为none?
这个 __get__()的触发条件是什么?
@FishC
最佳答案
2024-4-11 23:35:12
本帖最后由 isdkz 于 2024-4-11 23:37 编辑

拥有 __get__ 方法的对象会成为描述符,描述符的 __get__ 方法会在作为类属性被引用的时候触发,

并传入引用的实例对象作为描述符 __get__ 方法的第二个参数:
当引用对象为类的时候,描述符的__get__方法接收到的第二个参数即obj为None,因为引用对象为类,并没有实例,所以传入None
当引用对象为类的实例的时候,描述符的__get__方法接收到的第二个参数即obj为实例本身

代码举例:

# 定义一个描述符类
class Desc:
    def __get__(*args):
        print("描述符被引用")
        print("传入了参数:", args)

# 定义一个测试类
class Test:
    ...


# 实例化一个描述符对象
d = Desc()

# 实例化一个测试对象
t = Test()

# 描述符作为对象属性被引用的时候不会触发
t.d = d
print(t.d)            # 输出:<__main__.Desc object at 0x000001537CC37C70>
print(Test.d)       # 输出:AttributeError: type object 'Test' has no attribute 'd'

del t.d            # 删除之前的 t.d


Test.d = d
#  描述符作为类属性被类引用的时候,描述符的__get__方法接收到的第二个参数即obj为None
print(Test.d)                 
# 输出:
#描述符被引用
#传入了参数: (<__main__.Desc object at 0x000001537CC37C70>, None, <class '__main__.Test'>)

#  描述符作为类属性被实例引用的时候,描述符的__get__方法接收到的第二个参数即obj为实例本身
print(t.d)                 
# 输出:
#描述符被引用
#传入了参数: (<__main__.Desc object at 0x000001537CC37C70>, <__main__.Test object at 0x000001537CE23E20>, <class '__main__.Test'>)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2024-4-11 20:52:40 | 显示全部楼层
__get__()方法是用于描述符(descriptor)的特殊方法之一,它在描述符被获取时触发。在这段代码中,__get__()方法用于实现描述符协议,即当我们通过实例访问描述符时,它会被调用。

在这段代码中,obj代表实例对象,objtype代表实例所属的类。在描述符被获取时,__get__()方法会判断obj是否为None,以确定是通过实例还是类访问描述符。如果obj为None,则说明是通过类访问描述符,直接返回描述符本身;如果obj不为None,则说明是通过实例访问描述符,创建一个绑定方法,并返回该方法。

举个例子来说明:
class Function:
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return MethodType(self, obj)

class MyClass:
    func = Function()

my_obj = MyClass()
# 通过实例访问描述符
my_obj.func()
# 通过类访问描述符
MyClass.func()

当通过实例my_obj访问描述符func时,__get__()方法会被调用,此时obj为my_obj,objtype为MyClass。由于obj不为None,就会创建一个绑定方法并返回。

当通过类MyClass访问描述符func时,__get__()方法同样会被调用,但此时obj为None,所以直接返回描述符本身。

希望这个解答能对你有所帮助!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-11 23:35:12 | 显示全部楼层    本楼为最佳答案   
本帖最后由 isdkz 于 2024-4-11 23:37 编辑

拥有 __get__ 方法的对象会成为描述符,描述符的 __get__ 方法会在作为类属性被引用的时候触发,

并传入引用的实例对象作为描述符 __get__ 方法的第二个参数:
当引用对象为类的时候,描述符的__get__方法接收到的第二个参数即obj为None,因为引用对象为类,并没有实例,所以传入None
当引用对象为类的实例的时候,描述符的__get__方法接收到的第二个参数即obj为实例本身

代码举例:

# 定义一个描述符类
class Desc:
    def __get__(*args):
        print("描述符被引用")
        print("传入了参数:", args)

# 定义一个测试类
class Test:
    ...


# 实例化一个描述符对象
d = Desc()

# 实例化一个测试对象
t = Test()

# 描述符作为对象属性被引用的时候不会触发
t.d = d
print(t.d)            # 输出:<__main__.Desc object at 0x000001537CC37C70>
print(Test.d)       # 输出:AttributeError: type object 'Test' has no attribute 'd'

del t.d            # 删除之前的 t.d


Test.d = d
#  描述符作为类属性被类引用的时候,描述符的__get__方法接收到的第二个参数即obj为None
print(Test.d)                 
# 输出:
#描述符被引用
#传入了参数: (<__main__.Desc object at 0x000001537CC37C70>, None, <class '__main__.Test'>)

#  描述符作为类属性被实例引用的时候,描述符的__get__方法接收到的第二个参数即obj为实例本身
print(t.d)                 
# 输出:
#描述符被引用
#传入了参数: (<__main__.Desc object at 0x000001537CC37C70>, <__main__.Test object at 0x000001537CE23E20>, <class '__main__.Test'>)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-11 23:40:56 | 显示全部楼层
为了能给尊贵的鱼油解释,小人也是废了些心机的,如果有帮助的话请不要忘了给个最佳哦
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-12 15:38:39 | 显示全部楼层
class C:
    @staticmethod
    def funC():
        print('i love you')
这个不用语法糖的话,应该是怎么写的?@FishC
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-12 15:38:45 | 显示全部楼层
亦又 发表于 2024-04-12 15:38
class C:
    @staticmethod
    def funC():
     ...


在不使用语法糖的情况下,可以通过直接定义类的静态方法来实现相同的功能。以下是一个不使用语法糖的示例代码:

class C:
    def funC():
        print('i love you')
    funC = staticmethod(funC)

# 调用静态方法
C.funC()

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-12 17:37:21 | 显示全部楼层
class G:
    @classmethod
    @property
    def __doc__(cls):
        return f'A doc for {cls.__name__!r}'
这个不用语法糖的话,应该是怎么写的?@FishC
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-12 17:37:39 | 显示全部楼层
亦又 发表于 2024-04-12 17:37
class G:
    @classmethod
    @property
    def ...


嗨,@FishC!没有使用语法糖时,你可以这样编写代码:
class G:
    @classmethod
    @property
    def __doc__(cls):
        return 'A doc for ' + repr(cls.__name__)

这段代码与使用语法糖的效果相同。首先,`@property`装饰器将`__doc__()`方法转换为属性,以便我们可以直接访问它而不是调用它。然后,`@classmethod`装饰器表示该方法是一个类方法,可以通过类本身访问,而不是通过实例。

通过这两个装饰器的组合,我们可以将`__doc__()`方法转换为一个可以通过`G.__doc__`访问的类属性。在这个方法中,我们使用字符串格式化来生成一个文档字符串,它包含类名的引号括起来的表示形式,并返回该字符串。

希望这能帮到你!如果还有其他问题,请随时问。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-12 17:46:35 | 显示全部楼层

class G:
    @classmethod
    @property
    def __doc__(cls):
        return f'A doc for {cls.__name__}'
这个不用语法糖的话,应该是怎么写的?@FishC
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-12 17:46:43 | 显示全部楼层
亦又 发表于 2024-04-12 17:46
class G:
    @classmethod
    @property
    def ...


使用语法糖的目的是为了简化代码并提高可读性,所以将其转化为不使用语法糖的形式可能会显得有些繁琐。不过,你可以像下面这样来实现相同的功能:
class G:
    def __doc__(self):
        return f'A doc for {self.__class__.__name__}'

    __doc__ = property(__doc__)

在上面的代码中,我们定义了一个名为`__doc__`的普通函数,并将其赋值给了`G`类的`__doc__`属性。然后,我们使用内置的`property()`函数将`__doc__`属性转换为一个可读的属性。

希望能够帮到你!如果还有其他问题,请随时提问。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-12 17:49:40 | 显示全部楼层


class G:
    @classmethod
    @property
    def __doc__(cls):
        return f'A doc for {cls.__name__}'
这个不用语法糖的话,把语法糖还原怎么写的?@FishC
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-12 17:49:49 | 显示全部楼层
亦又 发表于 2024-04-12 17:49
class G:
    @classmethod
    @property
    def ...


要还原这段代码中的语法糖,可以将装饰器的写法改为常规的调用方式。

装饰器 `@classmethod` 可以改写成 `classmethod(函数名)`,装饰器 `@property` 可以改写成 `property(函数名)`。

所以,还原后的代码如下:
class G:
    def __doc__(cls):
        return f'A doc for {cls.__name__}'
    __doc__ = property(__doc__)
    __doc__ = classmethod(__doc__)

这样我们就还原了 `@classmethod` 和 `@property` 的装饰器写法。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-12 20:15:40 | 显示全部楼层
class ClassMethod:
     '''Emulate PyClassMethod_Type() in Objects/funcobject.c'''
     def __init__(self, f):
         self.f = f
     def __get__(self, obj, cls=None):
         if cls is None:
             cls = type(obj)
         if hasattr(type(self.f), '__get__'):
             return self.f.__get__(cls, cls)
         return MethodType(self.f, cls)
hasattr()函数不是用于判断对象是否拥有某个属性吗?
那这里的 hasattr()判断的是'__get__'属性,还是'__get__'方法?
@FishC
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-12 20:15:55 | 显示全部楼层
亦又 发表于 2024-04-12 20:15
class ClassMethod:
     '''Emulate PyClassMethod_ ...


`hasattr()`函数用于判断对象是否拥有某个属性或方法。在这段代码中,`hasattr(type(self.f), '__get__')`判断的是`self.f`所属类(即`type(self.f)`)是否拥有`__get__`方法。

在Python中,类和实例都可以有属性,而属性可以是数据属性或方法。当我们使用点语法访问、设置或删除属性时,实际上是对特殊方法`__getattribute__()`、`__setattr__()`和`__delattr__()`的调用。其中,`__getattribute__()`用于获取属性值,`__setattr__()`用于设置属性值,`__delattr__()`用于删除属性。

在这段代码中,`self.f`是一个函数对象,它是一个可调用对象,因此可以被当作方法来调用。`__get__()`方法是描述符协议中的一种特殊方法,用于处理属性访问。通过调用`self.f.__get__(cls, cls)`,我们可以将`self.f`绑定到类`cls`上,并返回绑定后的方法。

所以,`hasattr(type(self.f), '__get__')`的作用是判断`self.f`所属类是否实现了描述符协议中的`__get__()`方法。如果实现了,就使用`self.f.__get__(cls, cls)`进行属性访问;如果未实现,就使用`MethodType(self.f, cls)`进行属性访问。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-15 15:06

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表