返回顶部
首页 > 资讯 > 后端开发 > Python >听说你会 Python ?
  • 116
分享到

听说你会 Python ?

你会Python 2023-01-31 07:01:03 116人浏览 安东尼

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

摘要

前言 最近觉得 python 太“简单了”,于是在朋友面前放肆了一把:“我觉得 Python 是世界上最简单的语言!”。朋友嘴角闪过了一丝轻蔑的微笑(内心 OS:Naive!,作为一个 Python 开发者,我必须要给你一点人生经验,不然

前言

最近觉得 python 太“简单了”,于是在朋友面前放肆了一把:“我觉得 Python 是世界上最简单的语言!”。朋友嘴角闪过了一丝轻蔑的微笑(内心 OS:Naive!,作为一个 Python 开发者,我必须要给你一点人生经验,不然你不知道天高地厚!)于是朋友给我了一份满分 100 分的题,然后这篇文章就是记录下做这套题所踩过的坑。

1.列表生成器

描述

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

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

或者

class A(object):
    x = 1
    gen = (A.x for _ in xrange(10))  # gen=(x for _ in range(10))


if __name__ == "__main__":
    print(list(A.gen))

补充

感谢评论区几位提出的意见,这里我给一份官方文档的说明吧:
The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes comprehensions and generator expressions since they are implemented using a function scope. This means that the following will fail:

class A:
    a = 42
    b = list(a + i for i in range(10))

参考链接 Python2 Execution-Model:Naming-and-Binding , python3 Execution-Model:Resolution-of-Names。据说这是 PEP 227 中新增的提案,我回去会进一步详细考证。再次拜谢评论区 @没头脑很着急 @涂伟忠 @Cholerae 三位的勘误指正。

2.装饰器

描述

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

import time

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

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

@Timeit
def func():
    time.sleep(1)
    return "invoking function func"


if __name__ == '__main__':
    func()  # output: elapsed time is 1.00044410133

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

@Timeit
def func():
    time.sleep(1)
    return "invoking function func"


if __name__ == '__main__':
    func()  # output: elapsed time is 1.00044410133

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

答案

使用类装饰器后,在调用 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)

3.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 lambda
a()


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


c = C()
c.__len__ = lambda: 5
len(c)


# Traceback (most recent call last):
#  File "<stdin>", line 1, in <module>
# 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__ 中寻找属性。因此,就会出现如上所述的情况。

4.描述符

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

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

答案
1.第一个问题的其实很简单,如果你再运行一次 print(niche.math) 你就会发现,输出值是 75 ,那么这是为什么呢?这就要先从 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 属性时,依旧会触发描述符协议。

2.经过改良的做法,利用 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")

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 。

5.Python 继承机制

描述

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

class Init(object):
    def __init__(self, value):
        self.val = value


class Add2(Init):
    def __init__(self, val):
        super(Add2, self).__init__(val)
        self.val += 2


class Mul5(Init):
    def __init__(self, val):
        super(Mul5, self).__init__(val)
        self.val *= 5


class Pro(Mul5, Add2):
    pass


class Incr(Pro):
    csup = super(Pro)

    def __init__(self, val):
        self.csup.__init__(val)
        self.val += 1


p = Incr(5)
print(p.val)

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

6. 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 cv


sin1 = 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__


class A(object):
    __metaclass__ = SingleMeta


a1 = 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 = 2


a = 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__


class A(object):
    __metaclass__ = SingleMeta


print(A() is A())  # output: True

结语

感谢师父大人的一套题让我开启新世界的大门,恩,博客上没法艾特,只能传递心意了。说实话 Python 的动态特性可以让其用众多 black magic 去实现一些很舒服的功能,当然这也对我们对语言特性及坑的掌握也变得更严格了,愿各位 Pythoner 没事阅读官方文档,早日达到装逼如风,常伴吾身的境界。

--结束END--

本文标题: 听说你会 Python ?

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

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

猜你喜欢
  • 听说你会 Python ?
    前言 最近觉得 Python 太“简单了”,于是在朋友面前放肆了一把:“我觉得 Python 是世界上最简单的语言!”。朋友嘴角闪过了一丝轻蔑的微笑(内心 OS:Naive!,作为一个 Python 开发者,我必须要给你一点人生经验,不然...
    99+
    2023-01-31
    你会 Python
  • 听说你还不知道什么是 python?带你深入理解什么是 python
    文章目录 前言什么是pythonpython的由来我们为什么要学习python帮助python学习的网站 前言 各位朋友们,大家好。在之后的时间里,我将陆续为大家分享我在python学习...
    99+
    2023-09-02
    python 开发语言
  • 说说字符串转 OffSetDateTime 你真的会用吗
    字符串转 OffSetDateTime 你真的会用 要创建OffsetDateTime ,需要日期 (日,月和年), 时间 (小时,分钟,秒和纳秒)和偏移量 (与UTC的差异)。 如...
    99+
    2024-04-02
  • Python 你可能从未听说过的五种隐藏技巧
    人生苦短,快学Python1. ... 对象没错,你没看错,就是 "..."在Python中 ... 代表着一个名为 Ellipsis 的对象。根据官方说明,它是一个特殊值,通常可以作为空函数的占位符,或是用于Numpy中的切片操作。如:d...
    99+
    2023-05-14
    代码 Python 技巧
  • PHP 实时 API 容器,你有听说过吗?
    随着互联网技术的不断发展,越来越多的企业和个人开始使用 API 来提供服务或获取数据。而随着访问量的增加,传统的 API 架构已经不能满足实时性、可扩展性等需求,因此,实时 API 容器应运而生。 本文将介绍一种基于 PHP 的实时 AP...
    99+
    2023-10-01
    实时 api 容器
  • 谁说Python写GUI程序丑?那是你不会美化!
    在平时工作学习当中,我们经常会编写一些简单的 Python GUI 工具,以此来完成各种各样的自动化任务,比如批量处理文件,批量处理图片等等。当我们进行这些工具的编写之时,往往只关注了功能的实现,而忽略了页面的美化,这也使得在人们的眼中,P...
    99+
    2023-05-14
    Python GUI
  • 听说你还不知道什么是python?本文将带你发掘python的魅力并让你爱上他
    文章目录 前言什么是pythonpython的由来我们为什么要学习python帮助python学习的网站总结 前言 各位朋友们,大家好。龙叔我后台经常收到私信问什么是Python?有必要学...
    99+
    2023-09-18
    python 人工智能 开发语言 python入门 python入门教程 编程 python爬虫
  • 怎么用Python写个听小说的爬虫
    这篇文章主要介绍了怎么用Python写个听小说的爬虫的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用Python写个听小说的爬虫文章都会有所收获,下面我们一起来看看吧。书名和章节列表随机点开一本书,这个页面...
    99+
    2023-06-29
  • 听说你还不知道Java代码是怎么运行的?
    前言作为一名Java程序员,我们需要知道Java代码是怎么运行的。最近复习了深入理解Java虚拟机这本书,做了一下笔记,希望对大家有帮助,如果有不正确的地方,欢迎提出,感激不尽。在这里小编建了一个前端学习交流扣扣群:132667127,我自...
    99+
    2023-06-03
  • Windows 8.1会说7000多种话只要是人话它都能听懂
      Windows系统这几代对多国语言的支持越来越丰富,官方最新说法是,Windows 8.1已经可以支持7000多种世界各地的不同语言。基本上,只要你说的是“人话”,Windows都能&ldqu...
    99+
    2023-06-03
    win8.1 语言包 人话
  • 听说mysql中的join很慢?是你用的姿势不对吧
    目录驱动表与被驱动表驱动表和被驱动表有什么差异基于索引的joinjoin查询中如何选择驱动表不使用join,执行效率是否会更高join 是进行两个或多个数据表进行关联查询的过程中,经常使用的一种查询手段。提到join,你...
    99+
    2024-04-02
  • 详解如何用Python写个听小说的爬虫
    目录书名和章节列表音频地址下载完整代码总结在路上发现好多人都喜欢用耳机听小说,同事居然可以一整天的带着一只耳机听小说。小编表示非常的震惊。今天就用 Python 下载听小说 ...
    99+
    2024-04-02
  • Python这些问题你会吗?
    final作用域的代码一定会被执行吗? 正常的情况下,finally作用域的代码一定会被执行的,不管是否发生异常。哪怕是调用了sys.exit函数,finally也是会被执行的,那怎么样才能让finally代码不执行了。 import ...
    99+
    2023-01-31
    你会 Python
  • 一张图让你学会Python
    有编程基础的人一看就可以了解 Python 的用法了。真正的 30 分钟上手。国外一高手画的,现把它翻译成中文,入门超简单python入门神 ...
    99+
    2023-01-31
    让你 一张图 Python
  • 全面布局安全可靠 听听东软怎么说!
    随着互联网的加速渗透,网络可以说已经成为不可或缺的一部分,被大家称为继海、陆、空、天之外的第五大主权空间。和国家的领土不容侵犯一样,我国的网络空间也同样不容侵犯,由此一来网络安全也就相当重要。尤其是近年来,国家对于网络安全的重视程度提升到全...
    99+
    2023-06-04
  • PHP 多语言翻译:让你的应用会说各国语言
    随着全球化的发展,应用跨越国界和语言障碍变得至关重要。通过提供多语言翻译,企业可以扩大其用户群,提高用户体验并增加应用的竞争力。 多语言翻译的优势 扩大用户群:多語言翻譯使企業能夠接觸到更廣泛的受眾,包括不懂應用原生語言的用戶。 提升用戶...
    99+
    2024-04-02
  • 你是否听说过ASP缓存框架接口?这是如何工作的?
    ASP缓存框架接口是一种用于ASP应用程序的高效缓存机制,它可以显著提高应用程序的性能和响应速度。但是,对于大多数开发人员来说,这个概念可能还比较陌生。在本文中,我们将深入探讨ASP缓存框架接口的工作原理,并提供一些实用的演示代码来帮助你更...
    99+
    2023-06-14
    缓存 框架 接口
  • LeetCode 中的 Python 算法,你会了吗?
    LeetCode 是一个面向程序员的在线编程平台,它提供了大量的算法题目,让程序员在切磋中不断提升自己的编程技巧。Python 作为一门简洁、易学、高效的编程语言,自然也成为了 LeetCode 上的热门语言之一。本文将介绍一些常见的 Py...
    99+
    2023-09-07
    linux shell leetcode
  • explain都不会用,你还好意思说精通MySQL查询优化?
    这篇文章主要讲解了“explain都不会用,你还好意思说精通MySQL查询优化?”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“explain都不会用,你还好...
    99+
    2024-04-02
  • iPhone11系列你会如何选择?听听别人选11的8个理由,看看是否有道理
    随着iPhone11系列的发布以及发售已经有一段时间了,但是不少用户依旧不知道如何选择iPhone11系列,小酱同事最近就入手了一台iPhone11,小酱便向其请教为什么要选择入手iPhone11呢?他告诉小酱了这8个理由,小酱这就来分享给...
    99+
    2023-06-03
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作