自省
In computing, type introspection is the ability of a program to examine the type or properties of an object at runtime. Some programming languages possess this capability.
在計算機(jī)科學(xué)中,內(nèi)省是指計算機(jī)程序在運(yùn)行時(Run time)檢查對象(Object)類型(以及屬性等)的一種能力,通常也可以稱作運(yùn)行時類型檢查。
| 方法 | 作用 | 類型 |
|---|---|---|
| help() | 查看函數(shù)或者模塊用途的詳細(xì)說明 | 自省 |
| dir() | 返回對象所有屬性 | 自省 |
| type() | 查看對象類型 | 自省 |
| isinstance() | 判斷一個對象是否是一個已知的類型 | 自省 |
| issubclass() | 判斷一個類是不是另一個類的子類 | 自省 |
| id() | 返回地址值 | 自省 |
| callable() | 判斷對象是否可調(diào)用 | 自省 |
自省的常見使用方式
help()
help() 函數(shù)用于查看函數(shù)或模塊用途的詳細(xì)說明。主要在IDE環(huán)境下使用,接受任何擁有函數(shù)或者方法的對象,打印出對象所有的函數(shù)和文檔字符串。
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".
To get a list of available modules, keywords, symbols, or topics, type
"modules", "keywords", "symbols", or "topics". Each module also comes
with a one-line summary of what it does; to list the modules whose name
or summary contain a given string such as "spam", type "modules spam".
help>
# 可以繼續(xù)輸入 keywords、modules 等了解
對于自定義的類、函數(shù)、或者模塊等,也可以打印幫助信息。
class Demo:
"""
this is a Demo
"""
classVar = 0
def __init__(self):
self.var1 = 1
def output(self):
print(self.var1)
if __name__ == '__main__':
help(Demo)
dir()
dir() 函數(shù)可能是 Python 自省機(jī)制中最著名的部分了。它返回傳遞給它的任何對象的屬性名稱經(jīng)過排序的列表。如果不指定對象,則dir() 返回當(dāng)前作用域中的名稱。
dir() 函數(shù)適用于所有對象類型,包括字符串、整數(shù)、列表、元組、字典、函數(shù)、定制類、類實(shí)例和類方法。
# 查看當(dāng)前作用域中的屬性名稱
print(dir())
-------------------
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
# 查看 __builtins__ 模塊的屬性
print(dir(__builtins__))
——————————————————————
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
# 查看其他類型的屬性
print(dir('just a string')) # string
print(dir(42)) # Integer
print(dir([])) # List
print(dir(())) # Tuple
print(dir({})) # Dictionary
print(dir(dir)) # Function (functions are also objects)
將 dir() 運(yùn)用于定制類、類實(shí)例和屬性,下例可以說明 Python 自省能力的動態(tài)本質(zhì)
class Person(object):
"""Person class."""
def __init__(self, name, age):
self.name = name
self.age = age
def intro(self):
"""Return an introduction."""
return "Hello, my name is %s and I'm %s." % (self.name, self.age)
bob = Person("Robert", 35) # Create a Person instance
joe = Person("Joseph", 17) # Create another Person instance
joe.sport = "football" # Assign a new attribute to one instance
print(dir(Person)) # Attributes of the Person class
----------
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'intro']
print(dir(bob))
----------
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'intro', 'name']
print(dir(joe)) # Note that joe has an additional attribute 注意 joe 多了一個'sport' 屬性
----------
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'intro', 'name', 'sport']
print(bob.intro())
----------
Hello, my name is Robert and I'm 35.
print(dir(bob.intro))
----------
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
注意 dir() vs help()
- dir() : 只是得到方法或者屬性的名稱
- help() : 不但可以得到對象的方法和屬性名稱, 同時也可以得到這些方法或者屬性的使用方式的描述
type()
type() 函數(shù)屬于 Python 內(nèi)置函數(shù),通常用來查看某個變量的具體類型。其實(shí),type() 函數(shù)還有一個更高級的用法,即創(chuàng)建一個自定義類型(也就是創(chuàng)建一個類)。
type() 函數(shù)的語法格式有 2 種,分別如下:
- **type(obj) **
用來查看某個變量(類對象)的具體類型,obj 表示某個變量或者類對象。
- type(name, bases, dict)
用來創(chuàng)建類,其中 name 表示類的名稱;bases 表示一個父類的元組;dict 表示一個字典,即類內(nèi)定義的屬性方法和值組成的鍵值對。
# 示例
# 實(shí)例方法
def instancetest(self):
print("this is a instance method")
# 類方法
@classmethod
def classtest(cls):
print("this is a class method")
# 靜態(tài)方法
@staticmethod
def statictest():
print("this is a static method")
# 創(chuàng)建類
test_property = {"name": "tom", "instancetest": instancetest, "classtest": classtest, "statictest": statictest}
Test = type("Test", (), test_property)
# 創(chuàng)建對象
test = Test()
# 調(diào)用方法
print(test.name)
test.instancetest()
test.classtest()
test.statictest()
-----------------
tom
this is a instance method
this is a class method
this is a static method
注意:type vs object
type為對象的頂點(diǎn),所有對象都創(chuàng)建自type。
object為類繼承的頂點(diǎn),所有類都繼承自object。
python中萬物皆對象,一個python對象可能擁有兩個屬性,__class__ 和 __base__,__class__ 表示這個對象是誰創(chuàng)建的,__base__ 表示一個類的父類是誰。
print(object.__class__)
print(type.__base__)
---------------
<class 'type'>
<class 'object'>
- type類繼承自object
- object的對象創(chuàng)建自type
isinstance()
# 相關(guān)源碼
def isinstance(x, A_tuple): # real signature unknown; restored from __doc__
"""
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.
"""
pass
issubclass()
# 相關(guān)源碼
def issubclass(x, A_tuple): # real signature unknown; restored from __doc__
"""
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.
"""
pass
# 示例
class A:
pass
class B(A): # B類 繼承 A類
pass
b = B() # b 是 B類的一個實(shí)例
# is是去判斷這兩個是不是一個對象, 即id是否相同
# ==是判斷值是否相等
print(type(b)) # 求一個未知數(shù)據(jù)類型的對象
# <class '__main__.B'>
print(B)
# <class '__main__.B'>
print(id(type(b)))
# 2290407692200
print(id(B))
# 2290407692200
print(isinstance(b, B))
#True
print(isinstance(b, A)) # isinstance會根據(jù)繼承鏈去判斷
#True
print(type(b) is B) # 得到的是B的地址
#True
print(type(b) is A)
#False
print(isinstance(int, type)) # int 是type的子類
#True
print(isinstance(A, type)) # 類A是type的子類
#True
print(isinstance(True, int)) # 布爾是int的子類
#True
print(isinstance(b, type))
#False
print(issubclass(B, A)) # 類B 是 類A的子類 ,注意必須都傳入類名
#True
注意:
is vs ==
- is 是去判斷這兩個是不是一個對象, 即 id 是否相同
- == 是判斷值是否相等
- (ob1 is ob2) 等價于 (id(ob1) == id(ob2))
# 示例
a=[1,2,3]
b=[1,2,3]
c=a
print(id(a)) # 2866545727304
print(id(b)) # 2866545726984
print(id(c)) # 2866545727304
print(a is b) # False
print(c is a) # True
print((a==b)) # True
type() vs isinstance()
type() 用于求一個未知數(shù)據(jù)類型的對象,isinstance() 用于判斷一個對象是否是已知類型;
type() 不認(rèn)為子類是父類的一種類型,isinstance() 認(rèn)為子類是父類的一種類型,即子類對象也屬于父類類型
id()
# 相關(guān)源碼
def id(*args, **kwargs): # real signature unknown
"""
Return the identity of an object.
This is guaranteed to be unique among simultaneously existing objects.
(CPython uses the object's memory address.)
"""
pass
- id() 函數(shù)返回對象的唯一標(biāo)識符,標(biāo)識符是一個整數(shù)。
- CPython 中 id() 函數(shù)用于獲取對象的內(nèi)存地址。
callable()
# 相關(guān)源碼
def callable(i_e_, some_kind_of_function): # real signature unknown; restored from __doc__
"""
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.
"""
pass
- 該方法用來檢測對象是否可被調(diào)用,可被調(diào)用——指的是對象能否使用()括號的方法調(diào)用。
- 可調(diào)用對象,在實(shí)際調(diào)用也可能調(diào)用失?。坏遣豢烧{(diào)用對象,調(diào)用肯定不成功。
- 類對象都是可被調(diào)用對象,類的實(shí)例對象是否可調(diào)用對象,取決于類是否定義了
__call__方法。 - 也就是說,對于函數(shù)、方法、lambda 函式、 類以及實(shí)現(xiàn)了
__call__方法的類實(shí)例, 它都返回 True。
# 示例
class A:
pass
a = A()
print(callable(A)) #類A是可調(diào)用對象
#True
print(callable(a)) #實(shí)例a不可調(diào)用
#False
a() #調(diào)用實(shí)例a 會失敗
# Traceback (most recent call last):
# File "D:/BYA_Project/XTC_Develops/Python_json.py", line 97, in <module>
# a()
# TypeError: 'A' object is not callable
class B:
def __call__(self):
print("instance are callable now.")
b = B()
print(callable(B)) #類B是可調(diào)用對象
#True
print(callable(b)) #實(shí)例b是可調(diào)用對象
#True
b() #調(diào)用實(shí)例b成功
#instance are callable now.
反射
Introspection should not be confused with reflection, which goes a step further and is the ability for a program to manipulate the values, meta-data, properties and/or functions of an object at runtime.
也就是說自省和反射不是同一回事,自省是獲取對象類型的能力,而反射是操縱對象的值,元數(shù)據(jù),屬性和/或函數(shù)的能力。
在計算機(jī)學(xué)中,反射(英語:reflection)是指計算機(jī)程序在運(yùn)行時(runtime)可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。用比喻來說,反射就是程序在運(yùn)行的時候能夠“觀察”并且修改自己的行為。
在Python中反射非常簡單,用起來幾乎感覺不到與其他的代碼有區(qū)別,使用反射獲取到的函數(shù)和方法可以像平常一樣加上括號直接調(diào)用,獲取到類后可以直接構(gòu)造實(shí)例;不過獲取到的字段不能直接賦值,因?yàn)槟玫降钠鋵?shí)是另一個指向同一個地方的引用,賦值只能改變當(dāng)前的這個引用而已。
| 方法 | 作用 | 類型 |
|---|---|---|
| hasattr() | 判斷類方法或者類屬性是否存在,返回一個布爾值,存在返回True,反之返回Flase | 反射 |
| getattr() | 獲取屬性值或?qū)嵗椒?,如果其不存在,會拋出一個AttributeError異常 |
反射 |
| setattr() | 設(shè)置實(shí)例屬性的值,如果實(shí)例屬性不存在時,會自動給當(dāng)前實(shí)例添加該屬性 | 反射 |
| delattr() | 刪除實(shí)例的屬性或者實(shí)例的方法,當(dāng)其不存在時同樣會拋出一個AttributeError異常 |
反射 |
反射的應(yīng)用場景
# 示例
class Cat(object): # 類Cat 指向類對象
def __init__(self, name="kitty"):
self.name = name # 類屬性
def sayHi(self): # 類方法
print(self.name, "say Hi~~")
if __name__ == '__main__':
cat = Cat('KITTY') # 類的實(shí)例對象
print(cat.name) # 訪問對象屬性
cat.sayHi() # 訪問對象方法
if hasattr(cat, "name"): # 判斷
setattr(cat, "name", "tiger") # 設(shè)置
print(getattr(cat, "name")) # 獲取
delattr(cat, "name") # 刪除
print(hasattr(cat, "name")) # 判斷
-------------------
KITTY
KITTY say Hi~~
tiger
False
反射主要根據(jù)字符串去尋找類的屬性值,主要用于用戶交互,觸發(fā)類內(nèi)函數(shù)運(yùn)行,根據(jù)字符串動態(tài)的判斷,調(diào)用,添加/修改,刪除類或類的實(shí)例化對象中的方法或?qū)傩浴?/p>
反射在 web 框架中用的很多,通過解析url,執(zhí)行對應(yīng)不同的功能。
反射還可以用于動態(tài)導(dǎo)入模塊 ,如下所示:
# 示例
model = "requests"
function = "get"
obj = __import__(model)
request_get = getattr(obj, function)
response = request_get("http://example.com").text
print(response)
----------------------
<!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #f0f0f2;
margin: 0;
padding: 0;
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
div {
width: 600px;
margin: 5em auto;
padding: 2em;
background-color: #fdfdff;
border-radius: 0.5em;
box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
}
a:link, a:visited {
color: #38488f;
text-decoration: none;
}
@media (max-width: 700px) {
div {
margin: 0 auto;
width: auto;
}
}
</style>
</head>
<body>
<div>
<h1>Example Domain</h1>
<p>This domain is for use in illustrative examples in documents. You may use this
domain in literature without prior coordination or asking for permission.</p>
<p><a >More information...</a></p>
</div>
</body>
</html>