4.《python自省指南》学习

前言

  前面几篇博客我都是通过python自省来分析代码并试图得出结论。当然,仅仅通过自省能解决的问题有限,但遇到问题还是不自主的去用这个功能,觉得对于认识代码的含义还是有一定帮助的。而这些自省的知识我都是从python自省指南中学习的。所以,下文的内容基本参考自这份指南,看官可以直接转跳,笔者只是希望通过博客整理自己的思路,检验学习效果。

python自省是什么?

  在计算机编程中,自省是指编程语言检查某些事物以确定它是什么、有何属性及能做什么。

寻求帮助–help

  打开python的IDLE,就进入到了python解释器中,python解释器本身是被认为是一个主模块,然后在解释器提示符>>>下输入一些我们想了解的信息,所以首先我们会先寻求帮助,所以输入help,接着输入help(),我们就进入了help utility,然后循着提示keywords,modules,以了解python的关键字以及python自带的或者我们额外安装和定义的模块,如果要退出,输入’q’,然后回车。

  如果我们想了解某个对象(python里面所有对象都可以认为是对象),也可以求助也help(),不过要在括号里输入对象的名称,格式help(object),例如help(print),鉴于对象的自省内容太多,有的只粘贴出部分内容。

>>> help
Type help() for interactive help, or help(object) for help about object.
>>> help()

Welcome to Python 3.6's help utility!

If this is your first time using Python, you should definitely check out
the tutorial on the Internet at https://docs.python.org/3.6/tutorial/.

Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules.  To quit this help utility and
return to the interpreter, just type "quit".
...
help> keywords

Here is a list of the Python keywords.  Enter any keyword to get more help.

False               def                 if                  raise
None                del                 import              return
True                elif                in                  try
and                 else                is                  while
as                  except              lambda              with
assert              finally             nonlocal            yield
break               for                 not                 
class               from                or                  
continue            global              pass                

help> modules

Please wait a moment while I gather a list of all available modules...

PIL                 base64              idlelib             runpy
__future__          bdb                 idna                runscript
__main__            binascii            idna_ssl            sched
_ast                binhex              imaplib             scrolledlist
_asyncio            bisect              imghdr              search
_bisect             browser             imp                 
...
Enter any module name to get more help.  Or, type "modules spam" to search
for modules whose name or summary contain the string "spam".
>>> help('print')
Help on built-in function print in module builtins:

print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
    
    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current sys.stdout.
    sep:   string inserted between values, default a space.
    end:   string appended after the last value, default a newline.
    flush: whether to forcibly flush the stream.

 

sys模块

  sys模块提供关于python本身的详细内在信息。

>>> import sys
>>> sys.executable            #sys.executable 变量,它包含到 Python 解释器的路径;    
'D:\\Python36\\pythonw.exe' 
>>> sys.platform                #platform变量显示解释器所处的操作系统;
'win32'
>>> sys.version                 # ersion变量说明解释器的版本;
'3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 16:07:46) [MSC v.1900 32 bit (Intel)]'
>>> sys.argv[0]                 #vargv变量是一个包含命令行参数的列表;
''                              #argv[0]是脚本运行的路径,当我们以交互方式运行 Python 时,这个值是空字符串;
>>> sys.path                    #path 变量是模块搜索路径,Python 在导入期间将在其中的目录列表中寻找模块。最前面的空字符串 '' 是指当前目录;
['', 'D:\\Python36\\Lib\\idlelib', 'D:\\Python36\\python36.zip', 'D:\\Python36\\DLLs', 'D:\\Python36\\lib', 'D:\\Python36', 'D:\\Python36\\lib\\site-packages']
#变量sys.stdin、sys.stdout和sys.stderr是类似于文件的流对象,表示标准的UNIX概念:标准输入、标准输出和标准错误。
>>> sys.stdin                    #Python从sys.stdin获取输入(如,用于input中),
<idlelib.run.PseudoInputFile object at 0x02343F50>
>>> sys.stdout                     # 将输出打印到sys.stdout。
<idlelib.run.PseudoOutputFile object at 0x02343F70>
>>> sys.stderr
<idlelib.run.PseudoOutputFile object at 0x02343F90>
>>> 

'''一个标准数据输入源是sys.stdin。当程序从标准输入读取时,你可通过输入来提供文本,也可使用管道将标准输入关联到其他程序的标准输出。你提供给print的文本出现在sys.stdout中,向input提供的提示信息也出现在这里。写入到sys.stdout的数据通常出现在屏幕上,但可使用管道将其重定向到另一个程序的标准输入。错误消息(如栈跟踪)被写入到sys.stderr,但与写入到sys.stdout的内容一样,可对其进行重定向,例如:$ cat somefile.txt | python somescript.py | sort。可以认为,somescript.py从其sys.stdin中读取数据(这些数据是somefile.txt写入的),并将结果写入到其sys.stdout(sort将从这里获取数据)。'''
#somescript.py内容
import sys
text = sys.stdin.read()
words = text.split()
wordcount = len(words)
print('Wordcount:', wordcount)
#somefile.txt内容
Your mother was a hamster and your
father smelled of elderberries.

cat somefile.txt | python somescript.py的结果如下:
Wordcount: 11

dir()函数、__doc__文档字符串

  dir()函数返回传递给它的任何对象的属性名称经过排序的列表。如果不指定对象,则 dir() 返回当前作用域中的名称。

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>>
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_enablelegacywindowsfsencoding', '_getframe', '_git', '_home', '_xoptions', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright',
...
#在 dir() 示例中,有一个属性是 __doc__ 属性。这个属性是一个字符串,它包含了描述对象的注释,python称之为文档字符串或 docstring。文档字符串通常包含嵌入的换行 \n ,如何要使其变得易读,可以print出来
>>> sys.__doc__
"This module provides access to some objects used or maintained by the\ninterpreter and to functions that interact strongly with the interpreter.\n\nDynamic objects:\n\nargv -- command line arguments; argv[0] is the script pathname if known\npath -- module search path; path[0] is the script directory, else 
...
>>> print(sys.__doc__)
This module provides access to some objects used or maintained by the
interpreter and to functions that interact strongly with the interpreter.

Dynamic objects:

argv -- command line arguments; argv[0] is the script pathname if knownpath -- module search path; path[0] is the script directory, else
...

 

type()–检查python对象

  编程环境中的对象试图是模拟现实世界中的对象。实际的对象有一定的形状、大小、重量和其它特征,还会对其环境进行响应、与其它对象交互或执行任务。计算机中的对象则通过象文档、日程表和业务过程这样的抽象对象模拟现实对象。

  类似于实际的对象,几个计算机对象可能共享共同的特征,同时保持它们自己相对较小的变异特征。书籍是个抽象的概念,书店书籍的每个物理副本都可能有污迹、几张破损的书页或唯一的标识号。但每本书都只是原始模板的实例,并保留了原始模板的大多数特征。再比如说柏拉图的理想模型,可以说每种饼干都是来自于同一个模型

  对于面向对象的类和类实例也是如此。每个实例都是以类为模板,每个实例都有类的相同属性,但每个不同实例又有某些不同的地方比如属性值等,这就是面向对象。

  type() 函数有助于我们确定对象是字符串还是整数,或是其它类型的对象。它通过返回类型对象来做到这一点,可以将这个类型对象与 types 模块中定义的类型相比较:

>>> help(type)
Help on class type in module builtins:

class type(object)
 |  type(object_or_name, bases, dict)
 |  type(object) -> the object's type
 |  type(name, bases, dict) -> a new type
 |  
 |  Methods defined here:
 |  
 |  __call__(self, /, *args, **kwargs)
 |      Call self as a function.
 |  
 |  __delattr__(self, name, /)
 |      Implement delattr(self, name).
 |  
 |  __dir__(...)
 |      __dir__() -> list
 |      specialized __dir__ implementation for types
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __init__(self, /, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
...
>>> 
>>> type.__doc__
"type(object_or_name, bases, dict)\ntype(object) -> the object's type\ntype(name, bases, dict) -> a new type"
>>> dir(type)
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__', '__dir__', '__doc__', '__eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__instancecheck__', '__itemsize__', '__le__', '__lt__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__prepare__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__', '__subclasses__', '__subclasshook__', '__text_signature__', '__weakrefoffset__', 'mro']
>>> object
<class 'object'>
>>> object.__doc__
'The most base type'
>>> help(object)
Help on class object in module builtins:

class object
 |  The most base type
>>> 
>>> 
>>> help('class')
Class definitions
*****************

A class definition defines a class object (see section The standard
type hierarchy):

   classdef    ::= [decorators] "class" classname [inheritance] ":" suite
   inheritance ::= "(" [argument_list] ")"
   classname   ::= identifier

A class definition is an executable statement.  The inheritance list
usually gives a list of base classes (see Metaclasses for more
advanced uses), so each item in the list should evaluate to a class
object which allows subclassing.  Classes without an inheritance list
inherit, by default, from the base class "object"; hence,

   class Foo:
       pass

is equivalent to

   class Foo(object):
       pass

 

对象的名称–__name__、__main__

  并非所有对象都有名称,但那些有名称的对象都将名称存储在其 name 属性中。名称是从对象而不是引用该对象的变量中派生的。模块拥有名称,Python 解释器本身被认为是顶级模块或主模块。当以交互的方式运行 Python 时,局部 name 变量被赋予值 ‘main‘ 。同样地,当从命令行执行 Python 模块,而不是将其导入另一个模块时,其 name 属性被赋予值 ‘main‘ ,而不是该模块的实际名称。这样,模块可以查看其自身的 name 值来自行确定它们自己正被如何使用,是作为另一个程序的支持,还是作为从命令行执行的主应用程序。

>>> print.__name__
'print'
>>> import __main__
>>> help(__main__)
Help on module __main__:

NAME
    __main__

DATA
    __annotations__ = {}

FILE
    (built-in)
>>> __main__
<module '__main__' (built-in)>
>>> class A:
    pass

>>> A.__name__
'A'
>>> __name__
'__main__'
>>> A.__main__
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in <module>
    A.__main__
AttributeError: type object 'A' has no attribute '__main__'
>>> __main__
<module '__main__' (built-in)>
>>> help(__main__)
Help on module __main__:

NAME
    __main__

CLASSES
    builtins.object
        A
    
    class A(builtins.object)
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)

DATA
    __annotations__ = {}

FILE
    (built-in)


>>> class B:
    pass

>>> help(__main__)
Help on module __main__:

NAME
    __main__

CLASSES
    builtins.object
        A
        B
    
    class A(builtins.object)
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)
    
    class B(builtins.object)
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)

DATA
    __annotations__ = {}

FILE
    (built-in)
>>> dir(__main__)
['A', 'B', '__annotations__', '__builtins__', '__doc__', '__loader__', '__main__', '__name__', '__package__', '__spec__', 'sys']
>>> import json
>>> dir(__main__)
['A', 'B', '__annotations__', '__builtins__', '__doc__', '__loader__', '__main__', '__name__', '__package__', '__spec__', 'json', 'sys']

 

  在检查类实例的时候,我们常常会见到类似于这样的自省:<class ‘main.A’>,这里的__main__是一个模块,定义了顶层变量,A是该模块的一个属性,这个属性是一个类,这个类是A,当我们用type去检查A的实例a时,就有了<class ‘main.A’>,a实例属于主模块下的A类的实例。

>>> class A:
    pass

>>> type(A)
<class 'type'>
>>> a=A()
>>> type(a)
<class '__main__.A'>

 

id–标识,地址

  每个对象都有标识、类型和值。id表明变量所引用的对象,值得注意的是,可能有多个变量引用同一对象,同样地,变量可以引用看起来相似(有相同的类型和值),但拥有截然不同标识的多个对象。当更改对象时(如将某一项添加到列表),这种关于对象标识的概念尤其重要,id() 函数给任何给定对象返回唯一的标识符。

>>> help(id)
Help on built-in function id in module builtins:

id(obj, /)
    Return the identity of an object.
    
    This is guaranteed to be unique among simultaneously existing objects.
    (CPython uses the object's memory address.)

>>> id.__doc__
     
"Return the identity of an object.\n\nThis is guaranteed to be unique among simultaneously existing objects.\n(CPython uses the object's memory address.)"
>>> type(id)
     
<class 'builtin_function_or_method'>
>>> a=5
     
>>> b=a
     
>>> id(a)
     
1664672592
>>> id(b)
     
1664672592
>>> 

 

可调用、实例、子类

  可以用 callable() 函数测试对象的可调用性;在 type() 函数提供对象的类型时,还可以使用 isinstance() 函数测试对象,以确定它是否是某个特定类型或定制类的实例;issubclass() 函数使我们可以查看一个类是不是继承了另一个类;

>>> help(callable)
     
Help on built-in function callable in module builtins:

callable(obj, /)
    Return whether the object is callable (i.e., some kind of function).
    
    Note that classes are callable, as are instances of classes with a
    __call__() method.

>>> callable.__doc__
     
'Return whether the object is callable (i.e., some kind of function).\n\nNote that classes are callable, as are instances of classes with a\n__call__() method.'
>>> type(callable)
     
<class 'builtin_function_or_method'>
>>> callable(print)
     
True
>>> callable('a')
     
False
>>> help(isinstance)
     
Help on built-in function isinstance in module builtins:

isinstance(obj, class_or_tuple, /)
    Return whether an object is an instance of a class or of a subclass thereof.
    
    A tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to
    check against. This is equivalent to ``isinstance(x, A) or isinstance(x, B)
    or ...`` etc.

>>> isinstance.__doc__
     
'Return whether an object is an instance of a class or of a subclass thereof.\n\nA tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to\ncheck against. This is equivalent to ``isinstance(x, A) or isinstance(x, B)\nor ...`` etc.'
>>> type(callable)
     
<class 'builtin_function_or_method'>
>>> callable(43, int)
     
Traceback (most recent call last):
  File "<pyshell#35>", line 1, in <module>
    callable(43, int)
TypeError: callable() takes exactly one argument (2 given)
>>> type(isinstance)
     
<class 'builtin_function_or_method'>
>>> isinstance(45, int)
     
True
>>> isinstance('a', int)
     
False
>>> isinstance('a', str)
     
True
>>> help(issubclass)
     
Help on built-in function issubclass in module builtins:

issubclass(cls, class_or_tuple, /)
    Return whether 'cls' is a derived from another class or is the same class.
    
    A tuple, as in ``issubclass(x, (A, B, ...))``, may be given as the target to
    check against. This is equivalent to ``issubclass(x, A) or issubclass(x, B)
    or ...`` etc.

>>> issubclass.__doc__
     
"Return whether 'cls' is a derived from another class or is the same class.\n\nA tuple, as in ``issubclass(x, (A, B, ...))``, may be given as the target to\ncheck against. This is equivalent to ``issubclass(x, A) or issubclass(x, B)\nor ...`` etc."
>>> type(issubclass)
     
<class 'builtin_function_or_method'>
>>> class A:
     pass

     
>>> class B(A):
     pass

     
>>> issubclass(B, A)
     
True
>>> class C:
     pass

     
>>> issubclass(B, C)
     
False
>>>