返回顶部
首页 > 资讯 > 后端开发 > Python >Python调用机制是什么
  • 340
分享到

Python调用机制是什么

2023-06-02 06:06:39 340人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

这篇文章主要介绍“python调用机制是什么”,在日常操作中,相信很多人在Python调用机制是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python调用机制是什么”的疑惑有所帮助!接下来,请跟着小编

这篇文章主要介绍“python调用机制是什么”,在日常操作中,相信很多人在Python调用机制是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python调用机制是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

下面的代码会报错,为什么?

class A(object): x = 1 gen = (x for _ in xrange(10)) # gen=(x for _ in range(10))if __name__ == "__main__": print(list(A.gen))

答案

这个问题是变量作用域问题,在 gen=(x for _ in xrange(10)) 中 gen 是一个 generator,在 generator 中变量有自己的一套作用域,与其余作用域空间相互隔离。因此,将会出现这样的 NameError: name 'x' is not defined 的问题,那么解决方案是什么呢?答案是:用 lambda 。

class A(object): x = 1 gen = (lambda x: (x for _ in xrange(10)))(x) # gen=(x for _ in range(10))if __name__ == "__main__": print(list(A.gen))

装饰器

描述

我想写一个类装饰器用来度量函数/方法运行时间

import timeclass Timeit(object): def __init__(self, func): self._wrapped = func def __call__(self, *args, **kws): start_time = time.time() result = self._wrapped(*args, **kws) print("elapsed time is %s " % (time.time() - start_time)) return result

这个装饰器能够运行在普通函数上:

@Timeitdef func(): time.sleep(1) return "invoking function func"if __name__ == '__main__': func() # output: elapsed time is 1.00044410133

但是运行在方法上会报错,为什么?

class A(object): @Timeit def func(self): time.sleep(1) return 'invoking method func'if __name__ == '__main__': a = A() a.func() # Boom!

如果我坚持使用类装饰器,应该如何修改?

答案

使用类装饰器后,在调用 func 函数的过程中其对应的 instance 并不会传递给 __call__ 方法,造成其 mehtod unbound ,那么解决方法是什么呢?描述符赛高

class Timeit(object): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print('invoking Timer') def __get__(self, instance, owner): return lambda *args, **kwargs: self.func(instance, *args, **kwargs)

Python 调用机制

描述

我们知道 __call__ 方法可以用来重载圆括号调用,好的,以为问题就这么简单?Naive!

class A(object): def __call__(self): print("invoking __call__ from A!")if __name__ == "__main__": a = A() a() # output: invoking __call__ from A

现在我们可以看到 a() 似乎等价于 a.__call__() ,看起来很 Easy 对吧,好的,我现在想作死,又写出了如下的代码,

a.__call__ = lambda: "invoking __call__ from lambda"a.__call__()# output:invoking __call__ from lambdaa()# output:invoking __call__ from A!

请大佬们解释下,为什么 a() 没有调用出 a.__call__() (此题由 USTC 王子博前辈提出)

答案

原因在于,在 Python 中,新式类( new class )的内建特殊方法,和实例的属性字典是相互隔离的,具体可以看看 Python 官方文档对于这一情况的说明

For new-style classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary. That behaviour is the reason why the following code raises an exception (unlike the equivalent example with old-style classes):

同时官方也给出了一个例子:

class C(object): passc = C()c.__len__ = lambda: 5len(c)# Traceback (most recent call last):# File "", line 1, in # TypeError: object of type 'C' has no len()

回到我们的例子上来,当我们在执行 a.__call__=lambda:"invoking __call__ from lambda" 时,的确在我们在 a.__dict__ 中新增加了一个 key 为 __call__ 的 item,但是当我们执行 a() 时,因为涉及特殊方法的调用,因此我们的调用过程不会从 a.__dict__ 中寻找属性,而是从 tyee(a).__dict__ 中寻找属性。因此,就会出现如上所述的情况。

描述符

描述

我想写一个 Exam 类,其属性 math 为 [0,100] 的整数,若赋值时不在此范围内则抛出异常,我决定用描述符来实现这个需求。

class Grade(object): def __init__(self): self._score = 0 def __get__(self, instance, owner): return self._score def __set__(self, instance, value): if 0 <= 0="" 75="" 90="" value="" <="100:" self._score="value" else:="" raise="" valueerror('grade="" must="" be="" between="" and="" 100')="" exam(object):="" math="Grade()" def="" __init__(self,="" math):="" self.math="math" if="" __name__="=" '__main__':="" niche="Exam(math=90)" print(niche.math)="" #="" output="" :="" snake="Exam(math=75)" print(snake.math)="" snake.math="120" output:="" valueerror:grade="" 100!<="" code="">

看起来一切正常。不过这里面有个巨大的问题,尝试说明是什么问题

为了解决这个问题,我改写了 Grade 描述符如下:

class Grad(object): def __init__(self): self._grade_pool = {} def __get__(self, instance, owner): return self._grade_pool.get(instance, None) def __set__(self, instance, value): if 0 <= value="" <="100:" _grade_pool="self.__dict__.setdefault('_grade_pool'," {})="" _grade_pool[instance]="value" else:="" raise="" valueerror("fuck")<="" code="">

不过这样会导致更大的问题,请问该怎么解决这个问题?

答案

第一个问题的其实很简单,如果你再运行一次 print(niche.math) 你就会发现,输出值是 120 ,那么这是为什么呢?这就要先从 Python 的调用机制说起了。我们如果调用一个属性,那么其顺序是优先从实例的 __dict__ 里查找,然后如果没有查找到的话,那么一次查询类字典,父类字典,直到彻底查不到为止。好的,现在回到我们的问题,我们发现,在我们的类 Exam中,其 self.math 的调用过程是,首先在实例化后的实例的 __dict__ 中进行查找,没有找到,接着往上一级,在我们的类 Exam 中进行查找,好的找到了,返回。那么这意味着,我们对于 self.math 的所有操作都是对于类变量 math 的操作。因此造成变量污染的问题。那么该则怎么解决呢?很多同志可能会说,恩,在 __set__ 函数中将值设置到具体的实例字典不就行了。

那么这样可不可以呢?答案是,很明显不得行啊,至于为什么,就涉及到我们 Python 描述符的机制了,描述符指的是实现了描述符协议的特殊的类,三个描述符协议指的是 __get__ , ‘set‘ , __delete__ 以及 Python 3.6 中新增的 __set_name__ 方法,其中实现了 __get__ 以及 __set__ / __delete__ / __set_name__ 的是 Data descriptors ,而只实现了 __get__的是 Non-Data descriptor 。那么有什么区别呢,前面说了, 我们如果调用一个属性,那么其顺序是优先从实例的 __dict__ 里查找,然后如果没有查找到的话,那么一次查询类字典,父类字典,直到彻底查不到为止。 但是,这里没有考虑描述符的因素进去,如果将描述符因素考虑进去,那么正确的表述应该是我们如果调用一个属性,那么其顺序是优先从实例的 __dict__ 里查找,然后如果没有查找到的话,那么一次查询类字典,父类字典,直到彻底查不到为止。其中如果在类实例字典中的该属性是一个 Data descriptors ,那么无论实例字典中存在该属性与否,无条件走描述符协议进行调用,在类实例字典中的该属性是一个 Non-Data descriptors ,那么优先调用实例字典中的属性值而不触发描述符协议,如果实例字典中不存在该属性值,那么触发 Non-Data descriptor 的描述符协议。回到之前的问题,我们即使在 __set__ 将具体的属性写入实例字典中,但是由于类字典中存在着 Data descriptors ,因此,我们在调用 math 属性时,依旧会触发描述符协议。

经过改良的做法,利用 dict 的 key 唯一性,将具体的值与实例进行绑定,但是同时带来了内存泄露的问题。那么为什么会造成内存泄露呢,首先复习下我们的 dict 的特性,dict 最重要的一个特性,就是凡可 hash 的对象皆可为 key ,dict 通过利用的 hash 值的唯一性(严格意义上来讲并不是唯一,而是其 hash 值碰撞几率极小,近似认定其唯一)来保证 key 的不重复性,同时(敲黑板,重点来了),dict 中的 key 引用是强引用类型,会造成对应对象的引用计数的增加,可能造成对象无法被 GC ,从而产生内存泄露。那么这里该怎么解决呢?两种方法

第一种:

class Grad(object): def __init__(self): import weakref self._grade_pool = weakref.WeakKeyDictionary() def __get__(self, instance, owner): return self._grade_pool.get(instance, None) def __set__(self, instance, value): if 0 <= value="" <="100:" _grade_pool="self.__dict__.setdefault('_grade_pool'," {})="" _grade_pool[instance]="value" else:="" raise="" valueerror("fuck")<="" code="">

weakref 库中的 WeakKeyDictionary 所产生的字典的 key 对于对象的引用是弱引用类型,其不会造成内存引用计数的增加,因此不会造成内存泄露。同理,如果我们为了避免 value 对于对象的强引用,我们可以使用 WeakValueDictionary 。

第二种:在 Python 3.6 中,实现的 PEP 487 提案,为描述符新增加了一个协议,我们可以用其来绑定对应的对象:

class Grad(object): def __get__(self, instance, owner): return instance.__dict__[self.key] def __set__(self, instance, value): if 0 <= value="" <="100:" instance.__dict__[self.key]="value" else:="" raise="" valueerror("fuck")="" def="" __set_name__(self,="" owner,="" name):="" self.key="name

这道题涉及的东西比较多,这里给出一点参考链接,invoking-descriptors , Descriptor HowTo Guide , PEP 487 , what`s new in Python 3.6 。

Python 继承机制

描述

试求出以下代码的输出结果。

class Init(object): def __init__(self, value): self.val = valueclass Add2(Init): def __init__(self, val): super(Add2, self).__init__(val) self.val += 2class Mul5(Init): def __init__(self, val): super(Mul5, self).__init__(val) self.val *= 5class Pro(Mul5, Add2): passclass Incr(Pro): csup = super(Pro) def __init__(self, val): self.csup.__init__(val) self.val += 1p = Incr(5)print(p.val)

答案

输出是 36 ,具体可以参考 New-style Classes , multiple-inheritance

Python 特殊方法

描述

我写了一个通过重载 new 方法来实现单例模式的类。

class Singleton(object): _instance = None def __new__(cls, *args, **kwargs): if cls._instance: return cls._instance cls._isntance = cv = object.__new__(cls, *args, **kwargs) return cvsin1 = Singleton()sin2 = Singleton()print(sin1 is sin2)# output: True

现在我有一堆类要实现为单例模式,所以我打算照葫芦画瓢写一个元类,这样可以让代码复用:

class SingleMeta(type): def __init__(cls, name, bases, dict): cls._instance = None __new__o = cls.__new__ def __new__(cls, *args, **kwargs): if cls._instance: return cls._instance cls._instance = cv = __new__o(cls, *args, **kwargs) return cv cls.__new__ = __new__oclass A(object): __metaclass__ = SingleMetaa1 = A() # what`s the fuck

之前用这种方法给 __getattribute__ 打补丁的,下面这段代码能够捕获一切属性调用并打印参数

class TraceAttribute(type): def __init__(cls, name, bases, dict): __getattribute__o = cls.__getattribute__ def __getattribute__(self, *args, **kwargs): print('__getattribute__:', args, kwargs) return __getattribute__o(self, *args, **kwargs) cls.__getattribute__ = __getattribute__class A(object): # Python 3 是 class A(object,metaclass=TraceAttribute): __metaclass__ = TraceAttribute a = 1 b = 2a = A()a.a# output: __getattribute__:('a',){}a.b

试解释为什么给 getattribute 打补丁成功,而 new 打补丁失败。

如果我坚持使用元类给 new 打补丁来实现单例模式,应该怎么修改?

答案

其实这是最气人的一点,类里的 __new__ 是一个 staticmethod 因此替换的时候必须以 staticmethod 进行替换。答案如下:

class SingleMeta(type): def __init__(cls, name, bases, dict): cls._instance = None __new__o = cls.__new__ @staticmethod def __new__(cls, *args, **kwargs): if cls._instance: return cls._instance cls._instance = cv = __new__o(cls, *args, **kwargs) return cv cls.__new__ = __new__oclass A(object): __metaclass__ = SingleMetaprint(A() is A()) # output: True

到此,关于“Python调用机制是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: Python调用机制是什么

本文链接: https://lsjlt.com/news/229067.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Python调用机制是什么
    这篇文章主要介绍“Python调用机制是什么”,在日常操作中,相信很多人在Python调用机制是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python调用机制是什么”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-02
  • 什么是Java回调机制
    这篇文章主要讲解了“什么是Java回调机制”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“什么是Java回调机制”吧!一、回调回调分为同步回调和异步回调, 假如以买彩票的场景来模拟, 我买彩票...
    99+
    2023-06-20
  • Apache Mesos调度机制是什么
    本篇内容介绍了“Apache Mesos调度机制是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. ...
    99+
    2024-04-02
  • Python引用计数机制是什么
    Python的引用计数机制是一种自动内存管理机制,用于跟踪对象的引用数量。每个对象都有一个引用计数,当一个对象被创建时,引用计数为1...
    99+
    2023-10-25
    Python
  • React的调度机制原理是什么
    这篇文章主要介绍“React的调度机制原理是什么”,在日常操作中,相信很多人在React的调度机制原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”React的调度机制...
    99+
    2024-04-02
  • 什么是Python重试机制
    这篇文章主要讲解了“什么是Python重试机制”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“什么是Python重试机制”吧!为了避免由于一些网络或等其他不可控因素,而引起的功能性问题。比如在...
    99+
    2023-06-16
  • Laravel schedule调度的运行机制是什么
    这篇文章主要介绍“Laravel schedule调度的运行机制是什么”,在日常操作中,相信很多人在Laravel schedule调度的运行机制是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Larav...
    99+
    2023-06-29
  • linux任务调度机制指的是什么
    本篇内容主要讲解“linux任务调度机制指的是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“linux任务调度机制指的是什么”吧!linux的任务调度机制是指系统在某个事件执行的特定命令或程...
    99+
    2023-07-02
  • Netty的Handler链调用机制是什么及怎么组织
    这篇“Netty的Handler链调用机制是什么及怎么组织”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Netty的Hand...
    99+
    2023-07-05
  • python中什么是引用计数器机制
    本篇文章为大家展示了python中什么是引用计数器机制,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。python可以做什么Python是一种编程语言,内置了许多有效的工具,Python几乎无所不能,...
    99+
    2023-06-14
  • Laravel中schedule调度的运行机制是什么
    本篇内容介绍了“Laravel中schedule调度的运行机制是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Laravel 的 con...
    99+
    2023-06-26
  • python inter机制的意思是什么
    本篇内容主要讲解“python inter机制的意思是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“python inter机制的意思是什么”吧!说明inter机制是维护一个字典,已经创建的...
    99+
    2023-06-20
  • python内存管理机制是什么
    Python内存管理机制是自动化的。Python使用引用计数来跟踪和回收对象的内存。每个对象都有一个引用计数,当引用计数减少到0时,...
    99+
    2023-08-31
    python
  • python垃圾回收机制是什么
    Python的垃圾回收机制是自动化的,它使用了引用计数和循环垃圾收集两种方法。1. 引用计数:Python中的每个对象都有一个引用计...
    99+
    2023-08-14
    python
  • Python中什么是内存管理机制
    本篇文章给大家分享的是有关Python中什么是内存管理机制,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。python的数据类型有哪些python的数据类型:1. 数字类型,包括...
    99+
    2023-06-14
  • 什么是Python的内存管理机制
    一、引用计数 Python 的内存管理机制主要依赖于引用计数。每个对象都有一个引用计数器,用于记录当前对象被引用的次数。当一个对象被创建时,引用计数器的值为1,每当有一个新的引用指向该对象时,计数器加1;当一个引用不再指向该对象时,计数器减...
    99+
    2023-10-29
    管理机制 内存 Python
  • wpf调用python的方法是什么
    在WPF中调用Python方法,可以通过以下几种方式:1. 使用Python标准库的`subprocess`模块,通过调用Pytho...
    99+
    2023-10-12
    wpf python
  • python调用perl的方法是什么
    Python可以通过subprocess模块调用Perl方法。下面是一个示例代码:pythonimport subprocess# ...
    99+
    2023-10-18
    perl python
  • Python中的垃圾回收机制是什么
    本篇文章给大家分享的是有关Python中的垃圾回收机制是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。GC作为现代编程语言的自动内存管理机制,专注于两件事:1. 找到内存中...
    99+
    2023-06-17
  • python进程间的通信机制是什么
    本文小编为大家详细介绍“python进程间的通信机制是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“python进程间的通信机制是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。进程间通信表示进程之间的数...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作