zoukankan      html  css  js  c++  java
  • Python 拾遗

    偶尔复习,查漏补缺。(随手写的笔记,不建议看。。)

    不可变对象

    不可变对象常用在 参数共享/参数传递 上,好处很多,一是可以使用字符串池来节省空间,二是该对象可以安全地共享/传递,不会造成误修改问题。

    1. numbers
    2. string
    3. tuple
    4. frozenset

    a. 问题

    在使用*作为重复运算符时,如果目标是一个嵌套的可变对象,就会产生令人费解的问题:

    >>> a = [1,2,3]
    >>> b = a * 3
    >>> b
    [1, 2, 3, 1, 2, 3, 1, 2, 3]
    >>> b = [a] * 3  # nested
    >>> b
    [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
    >>> b[1][1] = 4
    >>> b
    [[1, 4, 3], [1, 4, 3], [1, 4, 3]]
    

    因为 * 并不是深拷贝,它只是简单地复制了 [a] 这个列表,里面的 [1,2,3] 都是同一个对象,所以改了一个,所有的都会改变。
    解决方法是不要使用 * 号,改用[a.copy() for i in range(3)] 执行深拷贝。如果不需要修改,请直接使用不可变对象。

    b. 奇技淫巧

    1. 序列分组(使用了浅拷贝的特性)
    >>> a = 'asdfghjkl'
    >>> iters = [iter(a)] * 3  # 这里是浅拷贝,列表中的三个元素都是同一个迭代器。重点就是这个
    >>> parts = zip(*iters)  # 参数解包
    >>> list(parts)
    [('a', 's', 'd'), ('f', 'g', 'h'), ('j', 'k', 'l')]
    

    作用域

    1. Python 中只有模块,类以及函数才会引入新的作用域,其它的代码块是不会引入新的作用域的。(而在 C/Java 中,任何一个 {} 块就构成一个局部作用域。另外 Julia 中 for/while/try-catch 都是局部作用域,但 if-else 又不是局部作用域。总之这些小差别要注意。)
    2. 局部变量可以与外部变量同名,并且在其作用域中,局部变量会覆盖掉外部变量。
      不知是出于实现简单或是性能,还是其他的原因,好像所有的语言都是这样的。其实我更希望变量的作用域覆盖会报错。
    3. 如果有函数与其他函数或变量(甚至某些保留字)同名,后定义的会覆盖掉先定义的。(这是因为 Python 中函数也是对象。而在 C/Java 中这是会报错的)

    此外,还有一个小问题,先看一个例子:

    >>> i = 4
    >>> def f():     # 单纯的从函数作用域访问外部作用域是没问题的
    ...     print(i)
    ... 
    >>> f()
    4
    

    再看一个问题举例:

    >>> i = 3
    >>> def f():
    ...     print(i)  # 这里应该是访问外部作用域
    ...     i = 5     # 可这里又定义了一个同名局部变量 i
    ... 
    >>> f()   # 于是就出错了
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in f
    UnboundLocalError: local variable 'i' referenced before assignment
    

    如果在内部作用域先访问外部作用域,再定义一个同名的局部变量,解释器就懵逼了。
    如果你其实想做的是改变全局变量 i 的值,就应该在开头声明 global i. 而如果 外部变量 i 不是存在于全局作用域,而是在某个闭合作用域内的话,就该用 nonlocal i

    inspect

    P.S. 类比 Java 的反射,但是要简单很多

    函数式

    1. map: Mapping Functions over Iterables
    2. zip:
    3. filter: Selecting Items in Iterables
    4. reduce: Combining Items in Iterables (这个现在在 functools 模块内,常和 operator 模块一起用)

    生成器

    1. yield from: 从另一个生成器返回值

    Built-in Functions

    Built-in Functions

    时间

    1. time: 时间模块,用的最多的应该就是 time.time() 了。
    2. datetime: 时间日期处理模块,支持年月日时分秒的算术运算。
    3. 性能分析:line_profile: 分析每行的运行时间。

    packages

    1. __all__: 用于控制 import * 的行为。

    2. 通过字符串加载包:importlib

    求值

    1. compile
      将 source 编译成 AST 对象,该对象可传给 eval 或 exec 执行。

    2. eval
      只接受单个表达式 (expression),evaluate it,并返回表达式的值。

    3. exec
      接受包含语句 (statement) 的代码段,execute it only for its side effects,无返回值。

    装饰器

    装饰器有两种:用函数定义的装饰器,还有用类定义的装饰器。函数装饰器最常用。

    装饰器可用于装饰函数,修改函数/类的某些行为,或者将函数注册到别的地方。

    1. 函数定义装饰器

    @decc
    def gg(xx):
        ...
    
    # 等同于
    def gg(xx)
    gg = decc(gg)
    

    带参的装饰器

    @decorator(A, B)
    def F(arg):
        ...
    
    F(99)
    
    # 等同于
    def F(arg):
        ...
    
    F = decorator(A, B)(F)      # Rebind F to result of decorator's return value
    F(99)                                # Essentially calls decorator(A, B)(F)(99)
    

    上面演示的是用函数定义的装饰器,也是最常用的装饰器。
    装饰器接收的参数可以是各种各样的,下面是一个带参的装饰器:

    @on_command("info")
    def get_info():
        return "这就是你需要的 info"
    
    def on_command(name: str):  # 调用此函数获得装饰器,这样就实现了带参装饰器
        def deco(func: Callable) -> Callable:  # 这个才是真正的装饰器
            # 将命令处理器注册到命令列表内
            return func  # 直接返回原函数,这样的话,多个装饰器就不会相互影响了。
        return deco
    
    # 上面的等同于:
    get_info = on_command("info")(get_info)  # on_command("info") 返回真正的装饰器
    

    如果你的 on_command 有通用的部分,还可以将通用的部分抽离出来复用:

    def _deco_maker(event_type: str) -> Callable:  # 调用这个,获取 on_xxx 的 deco_deco,
        def deco_deco(self) -> Callable:   # 这个对应 on_xxx
            def deco(func: Callable) -> Callable: # 这个才是真正的装饰器
                # do something 
                return func  # 返回原函数
    
            return deco
    
        return deco_deco
    

    我们知道 Python 的类实际上是可以很方便的修改的,因此函数装饰器也能用于装饰类,修改类的某些行为。

    def log_getattribute(cls):
        # Get the original implementation
        orig_getattribute = cls.__getattribute__
    
        # Make a new definition
        def new_getattribute(self, name):
            print('getting:', name)
            return orig_getattribute(self, name)
    
        # Attach to the class and return
        cls.__getattribute__ = new_getattribute  # 修改了被装饰类 cls 的 __getattribute__
        return cls
    
    # Example use
    @log_getattribute
    class A:
        def __init__(self,x):
            self.x = x
        def spam(self):
            pass
    

    2. 类定义装饰器

    类定义装饰器和函数定义装饰器的使用方式完全一致。它也可以用于装饰函数或者类。

    那么为啥还需要类定义装饰器呢?它的优势在于类是可以继承的,这样的话,就能用继承的方式定义装饰器,将通用部分定义成超类。

    类定义装饰器的定义方法如下:

    # PythonDecorators/entry_exit_class.py
    class entry_exit(object):
    
        def __init__(self, f):
            self.f = f
    
        def __call__(self):  #关键在于这个函数,它使此类的对象变成 Callable
            print("Entering", self.f.__name__)
            self.f()
            print("Exited", self.f.__name__)
    
    @entry_exit
    def func1():
        print("inside func1()")
    
    # 上面的装饰器相当于
    func1 = entry_exit(func1)  # 从这里看的话,装饰器的行为完全一致
    
    # 接下来调用该函数(实际上是调用了 entry_exit 对象的 call 函数)
    func1()
    

    输出结果如下:

    Entering func1
    inside func1()
    Exited func1
    

    OOP

    1. 调用超类方法:

      • 直接通过超类名.__init__(self,xx)调用
      • 通过super(__class__, self).__init__()调用。
        (Python3 可直接用 super().__init__()
        但是要搞清楚,super() 方法返回的是一个代理类。另外被代理的类也不一定是其超类。如果不清楚这些差别,最好还是显式用方法一最好。
    2. 抽象超类:@abstractmethod

    3. @staticmethod @classmethod 与 Java 的 static 方法对比
      python的类方法、静态方法,与java的静态方法:

      1. java 中 constants、utils 这样的静态类,对应的是python的一个模块(文件),类属性对应模块的全局属性,静态方法对应模块的函数

      2. 对于 java 中需要访问类属性的静态方法,如果它不属于第一类,应该用 @classmethod 实现它。classmethod最大的特点就是一定有一个 cls 传入。这种方法的主要用途是实现工厂函数。

      3. 对于不需要访问任何类属性,也不属于第一类的方法,应该用 @staticmathod 实现。这种方法其实完全不需要放到类里面,它就是一个独立的函数。(仍然放里面,是为了把功能类似的函数组织到一起而已。)

    4. __slots__: 属性导出,不在该列表内的属性,若存在则为只读。不存在的话,就不存在。。
      6. __getattr__: 拦截对不存在的属性的访问,可用于实现动态分配属性。

    5. __getattribute__: 和上面相同,但是它拦截对所有属性的访问,包括对已存在的属性的访问。

    6. @property: 提供对属性访问的安全检查

    7. descriptor: get set delete 控制对类的访问。(上面的 getattr 等是控制对类的属性的访问)

    8. 类构造器 __new__:在 __init__ 之前运行,它接收一个 cls 参数,然后使用它构造并返回类实例 self

    9. 类方法的 cls 即是当前类,是 type 的实例,cls.xxx<类名>.xxx 调用结果是一致的。而 self 由 __new__ 构造,是 cls 的实例。

    元类 metaclasses

    元类,也就是用于创建class 的 class,算是很高级的话题了(If you wonder whether you need metaclasses, you don’t )
    元类的工作流程:

    1. 拦截类的创建
    2. 修改类
    3. 返回修改之后的类
      详细直接看 http://blog.jobbole.com/21351/ 吧。

    查看源码

    Python 很多功能是 C 写的,直接从 Pycharm 中只能看到自动生成的文档。
    对一般的标准库的模块,查看方法是很简单的:直接通过 __file__ 属性就能看到。
    而对于 builtins 模块,都在这个文件里。

  • 相关阅读:
    并查集模板
    css margin 负值 合并盒子边框线
    滑动门原理
    精灵图制作
    css 单行文本超出用 省略号表示...
    css vertical-align 垂直对齐 解决图片空白缝隙
    css 鼠标样式 取消input 框 轮廓线 防止用户拖拽文本域
    css 显示与隐藏
    css 圆角矩形用法
    css 定位详解
  • 原文地址:https://www.cnblogs.com/kirito-c/p/9129456.html
Copyright © 2011-2022 走看看