美文网首页python热爱者Python新世界
一篇文章踩遍Python中的坑

一篇文章踩遍Python中的坑

作者: 48e0a32026ae | 来源:发表于2018-11-30 15:03 被阅读0次

当然在学习Python的道路上肯定会困难,没有好的学习资料,怎么去学习呢?

所以小编准备了一份零基础入门Python的学习资料。

学习Python中有不明白推荐加入交流群

                号:516107834

                群里有志同道合的小伙伴,互帮互助,

                群里有不错的学习教程!

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

In [1]: class A(object):

...: x = 1

...: gen = (x for _ in xrange(10))

...:

In [2]: if __name__ == "__main__":

...: print(list(A.gen))

...:

---------------------------------------------------------------------------

NameError Traceback (most recent call last)

in ()

1 if __name__ == "__main__":

----> 2 print(list(A.gen))

3

in ((_,))

1 class A(object):

2 x = 1

----> 3 gen = (x for _ in xrange(10))

4

NameError: global name 'x' is not defined

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

In [7]: class A(object):

...: x = 1

...: gen = (lambda x: (x for _ in xrange(10)))(x)

...:

In [9]: if __name__ == "__main__":

print(list(A.gen))

...:

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

装饰器

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

In [10]: import time

In [11]: class 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

....:

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

In [12]: @Timeit

....: def func():

....: time.sleep(2)

....: return "invoking function func"

....:

In [13]: if __name__ == "__main__":

....: func()

....:

elapsed time is 2.00392723083

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

In [14]: class A(object):

....: @Timeit

....: def func(self):

....: time.sleep(1)

....: return "invoking method func"

....:

In [15]: if __name__ == "__main__":

....: a = A()

....: a.func()

....:

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

in ()

1 if __name__ == "__main__":

2 a = A()

----> 3 a.func()

4

in __call__(self, *args, **kws)

4 def __call__(self, *args, **kws):

5 start_time = time.time()

----> 6 result = self._wrapped(*args, **kws)

7 print("elapsed time is %s ")%(time.time() - start_time)

8 return result

TypeError: func() takes exactly 1 argument (0 given)

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

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

In [16]: 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)

....:

In [17]: class A(object):

....: @Timeit

....: def func(self):

....: time.sleep(1)

....: return "invoking method func"

....:

In [18]: if __name__ == "__main__":

....: a = A()

....: a.func()

....:

Python调用机制

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

In [19]: class A(object):

....: def __call__(self):

....: print("invoking __call__ from A!")

....:

In [20]: if __name__ == "__main__":

....: a = A()

....: a()

....:

invoking __call__ from A!

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

In [21]: a.__call__ = lambda: "invoking __call__ from lambda"

In [22]: a.__call__()

Out[22]: 'invoking __call__ from lambda'

In [23]: a()

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):

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

In [24]: class C(object):

....: pass

....:

In [25]: c = C()

In [26]: c.__len__ = lambda: 5

In [27]: len(c)

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

in ()

----> 1 len(c)

TypeError: object of type 'C' has no len()

In [28]: c.__len__

Out[28]: >

In [29]: c.__len__()

Out[29]: 5

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

相关文章

  • 一篇文章踩遍Python中的坑

    当然在学习Python的道路上肯定会困难,没有好的学习资料,怎么去学习呢? 所以小编准备了一份零基础入门Pytho...

  • android使用appium的自动化

    PS:看了别人写的文章博客,感觉踩了很多坑,自己写一遍整理下踩过的坑。 1.安装adb 通过homebrew安装 ...

  • 踩坑

    我们在这个世间,就是把要踩的坑,把好的坑,把坏的坑,都踩一遍!都经历一遍,都尝一遍! 我们踩过之后就会知道,不要进...

  • Python踩坑记录

    发现上一篇Python记录写到后面像是我的学习笔记了,还是单开一个记录一下踩坑吧。 1.字典python3中,使用...

  • Vue踩坑实录(二)

    在上一篇中说了一下踩过的前三个坑,剩下的坑就在这篇中全部搞定吧。Vue踩坑实录(一) Vue-cli .js?.V...

  • [Kotlin/Native] 你想知道的都在这里了

    再一次过来帮大家踩坑了,Kotlin/Native 虽然很方便,但是坑依然不少,通过这一篇文章踩完吧,希望以后不要...

  • Win10 + Appium+夜神/MuMu模拟器环境搭建

    概述(建议搭建前先完整看一遍,心里会有一个大概的印象,毕竟我踩过坑) 1.安装Python3 2.安装Python...

  • 算法踩坑6-二叉搜索树排序

    背景 接上面五篇文章算法踩坑-快速排序 算法踩坑2-插入排序 算法踩坑3-堆排序 算法踩坑4-冒泡排序 ...

  • IDEA 搭建 JDK 源码工程简记

    好记性不如烂笔头,记一篇 IDEA 中搭建 JDK 源码阅读工程的文章,防止下次踩坑,此次 JDK 版本为:1.8...

  • SpringCloud Alibaba之Seata入门以及踩坑(

    前言 上一篇文章:➣SpringCloud Alibaba之Seata入门以及踩坑(一)[https://www....

网友评论

    本文标题:一篇文章踩遍Python中的坑

    本文链接:https://www.haomeiwen.com/subject/jjjhcqtx.html