查漏补缺--python小细节拾遗(二)

废话不多说,继续补漏:

1.python的命名空间和作用域

  命名空间:

    a.全局命名空间: pycharm创建一个py文件,此时整个文件为全局命名空间;

    b.局部命名空间: 此时在py文件中创建一个函数,函数中即为局部命名空间;

    c.内置命名空间: python存放内置函数和类的空间,如存放min,list函数等;

  作用域:

    a.全局作用域: 包含 全局命名空间 和 内置命名空间.

    b.局部作用域: 包含 局部命名空间.

    c.闭包: 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure),不需要一定返回内层函数;

  程序运行时命名空间加载顺序:

    c ----> b ----> a

  程序运行时取值作用域顺序:(就近原则)

1 a = 5
2 def func(a):
3     a = 10
4     print(a)
5 func(a)    # 10
6 print(a)    # 5
就近原则

2.可迭代对象与迭代器与生成器

  python官网术语大全: https://docs.python.org/zh-cn/3/glossary.html

  a.可迭代对象: (Iterable, 摘用"流畅的python"中的定义)

    使用iter内置函数可以获取迭代器的对象.

    如果对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的.

    序列都可以迭代.(标准序列实现了: __iter__方法和__getitem__方法)

    实现了__getitem__方法,而且其参数是从零开始的索引,这种对象也可以迭代.

    检测可迭代对象方法:

      方法一: 调用iter(),返回一个迭代器对象,若报错,则为不可迭代对象.(python3.4以上推荐)

      方法二: from collections import abc  isinstance(x, abc.Iterable)  (python3.4以上不推荐,其忽略了__getitem__方法)

    python中的字符串,列表,字典,集合等均为可迭代对象,可迭代对象不能直接取值,需要转换为迭代器才能取值.

    注: for循环本质上调用了可迭代对象的__iter__()方法,得到了该对象对应的迭代器对象,然后无限调用__next__()方法,

       得到对象中的每一个元素.直到StopIteration异常,代表迭代器中已无下一个元素,for循环自动处理该异常,跳出循环.

    这里可以想想怎么用while循环来模拟for循环取字符串中每个元素的值,

  b.迭代器(Iterator): 

    定义: 实现了无参数的__next__方法,返回序列中的下一个元素;如果没有元素,抛出StopIteration异常.

    Python中的迭代器还实现了__iter__方法(必须return self),方便在应该使用可迭代对象的地方使用迭代器),因此迭代器也可以迭代,也是可迭代对象.

 1 s = 'ABC'
 2 for char in s:
 3     print(char)
 4     
 5 # while循环模拟
 6 iterator = iter(s)
 7 while True:
 8     try:
 9         print(next(iterator))
10     except StopIteration:
11         del iterator    # 释放迭代器引用,废弃迭代器对象
12         break
while循环模拟迭代

    检查迭代器最靠谱的方法:

      from collections import abc

      isinstance(x, abc.Iterator)

  c.生成器: 通常指的是生成器函数,某些情况下也可能是指 生成器迭代器

    生成器函数: 含有yield表达式的函数;(reversed(seq), zip, enumarate, filter, map,另外itertools模块中一堆鬼鬼函数)

    生成器迭代器: 生成器函数创建的对象.

1 def func():
2     for i in range(10):
3         yield i
4 g = func()  # func为生成器函数, g为生成器迭代器
5 print(next(g))  # next取值
6         
生成器

 

  d.生成器表达式: 将列表表达式的[]用()替换;

    这里需要注意的是:调用生成器表达式时,内部运行从起始开始,而列表表达式内部已经运行完毕.

1 def func1():
2     return [lambda x: i*x for i in range(4)]
3 
4 def func2():
5     return (lambda x: i*x for i in range(4))
6 
7 print([i(2) for i in func1()])    # [6, 6, 6, 6]
8 print([i(2) for i in func2()])    # [0, 2, 4, 6]
表达式对比

  总结与对比:

    1.迭代器需要可迭代对象转换才能取值,可迭代对象是十分占内存的;

    2.生成器在调用时才创建,本质上十分节省内存;

    3.生成器和迭代器均可以统一用next(x)来进行元素调用;

 

3.继承相关

  新式类和经典类

    新式类: 继承自object类,python3中所有类均为新式类;(多继承时遵循广度优先,根据mro列表来进行继承关系顺序)

    经典类: 不继承自object类,仅存于python2中;(多继承时遵循深度度优先,无super()方法和mro列表)  

 

 1 class A:
 2     def func(self):
 3         print("A")
 4 
 5 
 6 class B(A):
 7     def func(self):
 8         super().func()
 9         print("B")
10 
11 
12 class C(A):
13     def func(self):
14         super().func()
15         print("C")
16 
17 
18 class D(B, C):
19     def func(self):
20         super().func()
21         print("D")
22 
23 
24 d = D()
25 print(D.mro())  # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
26 d.func()        # 返回结果和上面mro列表保持一致,遵循广度优先原则
多继承和mro

4.方法

  只有能被对象调用的类中的方法才能成为方法.(学术装B利器)

 1 class A:
 2     def func1(self): pass
 3 
 4     @staticmethod
 5     def func2():pass
 6 
 7 a = A()
 8 from types import MethodType, FunctionType
 9 
10 print(isinstance(a.func1, MethodType))      # True, 类实例对象调用才是方法
11 print(isinstance(a.func1, FunctionType))    # False
12 print(isinstance(a.func2, MethodType))      # False,类实例调用静态方法也不是方法
13 print(isinstance(a.func2, FunctionType))    # True, 而是函数
14 print(isinstance(A.func1, MethodType))      # False
15 print(isinstance(A.func1, FunctionType))    # True
16 print(isinstance(A.func2, MethodType))      # False
17 print(isinstance(A.func2, FunctionType))    # True, 静态方法永远都是函数
方法和函数的区分

5.python中的多态

  多态: 一种事物有多种形态.

  多态的存在其实本身就是为了解决由于规定了传参类型而导致需要不断创建函数或类来涵盖更多传参的问题啦,因为继承,所以多态.

  而python不同于其它语言,其传参相当自由,且python3中所有类均为object类的子类,所以其语言本身就是多态的体现.

  注: 鸭子类型(多态具象化): 你看起来像鸭子,所以我认为你就是鸭子.

     描述: 不是明确的通过继承实现的多态,而是通过一个模糊的概念来判断这个函数能不能接受这个类型的参数;

     python中最典型的鸭子类型: print()函数,传入啥都能打印.

6.反射

  定义: 通过字符串来获得属性(包括类方法,变量等),简单举例就是给个'abc'的字符串,返回abc()方法.

      相比于eval的不稳定性,反射让我们装B更有底气了.

  以下举例说明:

  a.反射类中的变量: 主要有: 静态属性, 类方法, 静态方法

 1 class Foo:
 2     school = "QJZX"
 3     country = 'China'
 4     language = 'Chinese'
 5     
 6     def a():
 7         print("a")
 8 
 9 print(getattr(Foo, 'school'))  # QJZX,获取属性值
10 getattr(Foo, 'a')()         # 打印a,getattr(Foo, 'a')等同于Foo.a,加括号直接调用
11 print(hasattr(Foo, 'a'))                # True
12 print(hasattr(Foo, 'country'))       # True
13 print(hasattr(Foo, 'language'))     # True
14 print(hasattr(Foo, 'b'))                # False
反射类中变量

  b.反射实例化对象中的变量

 1 class Foo:
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5     
 6     def eating(self):
 7         print("%s is eating shit" % self.name)
 8 
 9 sss = Foo("hxb", 3)
10 print(getattr(sss, 'name'))     # hxb, 获取实例对象属性
11 getattr(sss, 'eating')()        # hxb is eating shit, 调用类方法
反射实例中变量

  c.反射模块中的变量

1 import os
2 
3 os.rename("文件名1", '文件名2')                    # 改名
4 getattr(os, 'rename')('文件名2', '文件名1')      # 再改回来
反射模块中变量

  d.反射本py文件中的变量

 1 a = 1
 2 b = 2
 3 name = 'alex'
 4 
 5 def merry_me():
 6     print("No!")
 7 
 8 class Foo: pass
 9 
10 import sys
11 
12 print(sys.modules)                              # 加载模块的内置内容
13 print(sys.modules['__main__'])                  # 本文件的命名空间
14 print(sys.modules['__main__'].a)                # 显示a的值为1
15 print(__name__, __name__ == '__main__')         # 内置变量,为字符串
16 print(sys.modules[__name__])                    # 反射本文件中的变量,固定的使用这个命名空间
17 getattr(sys.modules[__name__], 'merry_me')()    # 调用merry_me函数
18 print(getattr(sys.modules[__name__], 'Foo'))    # 反射类
19 obj = getattr(sys.modules[__name__], 'Foo')()   # 实例化类对象
反射本py文件中变量

 End!

posted @ 2020-07-03 13:50  葛小天  阅读(204)  评论(0)    收藏  举报