![](/img/trans.png)
[英]How to create an object by class and get information from the list in Python?
[英]Python: How to retrieve class information from a 'frame' object?
是否可以從框架 object 中檢索任何 class 信息? I know how to get the file (frame.f_code.co_filename), function (frame.f_code.co_name) and line number (frame.f_lineno), but would like to be able to also get the name of the class of the active object框架的實例(如果不在實例中,則為無)。
我不相信,在框架對象級別,有任何方法可以找到已被調用的實際 python 函數對象。
但是,如果您的代碼依賴於通用約定:命名方法的實例參數self
,那么您可以執行以下操作:
def get_class_from_frame(fr):
import inspect
args, _, _, value_dict = inspect.getargvalues(fr)
# we check the first parameter for the frame function is
# named 'self'
if len(args) and args[0] == 'self':
# in that case, 'self' will be referenced in value_dict
instance = value_dict.get('self', None)
if instance:
# return its class
return getattr(instance, '__class__', None)
# return None otherwise
return None
如果您不想使用getargvalues
,您可以直接使用frame.f_locals
而不是value_dict
和frame.f_code.co_varnames[:frame.f_code.co_argcount]
而不是args
。
請記住,這仍然僅依賴於約定,因此它不可移植,並且容易出錯:
self
作為第一個參數名稱,那么get_class_from_frame
將錯誤地返回第一個參數的類。@classmethod
和@staticmethod
不會接受self
參數,而是使用描述符實現的。根據您到底想要做什么,您可能需要花一些時間深入挖掘並找到所有這些問題的解決方法(您可以檢查返回的類中是否存在 frame 函數並共享相同的源,檢測描述符調用是可能的,類方法等也一樣。)
這有點短,但大致相同。 如果類名不可用,則返回 None。
def get_class_name():
f = sys._getframe(1)
try:
class_name = f.f_locals['self'].__class__.__name__
except KeyError:
class_name = None
return class_name
我剛剛遇到了這篇文章,因為我遇到了同樣的問題。 然而,由於已經列出的所有原因,我並不認為“自我”方法是一個可接受的解決方案。
下面的代碼演示了一種不同的方法:給定一個框架對象,它在全局變量中搜索具有匹配成員名稱和代碼塊的對象。 搜索很難窮盡,因此可能不會發現所有類,但找到的類應該是我們正在尋找的類,因為我們會驗證匹配代碼。
代碼的目標是在函數名前面加上它的類名,如果找到的話:
def get_name( frame ):
code = frame.f_code
name = code.co_name
for objname, obj in frame.f_globals.iteritems():
try:
assert obj.__dict__[name].func_code is code
except Exception:
pass
else: # obj is the class that defines our method
name = '%s.%s' % ( objname, name )
break
return name
請注意使用__dict__
而不是getattr
來防止捕獲派生類。
進一步注意,如果self = frame.f_locals['self']; obj = self.__class__
可以避免全局搜索self = frame.f_locals['self']; obj = self.__class__
self = frame.f_locals['self']; obj = self.__class__
給出匹配,或者obj in self.__class__.__bases__
任何obj in self.__class__.__bases__
或更深,所以肯定有優化/混合的空間。
如果方法是類方法,則該類將是第一個參數。 如果每個調用堆棧幀存在第一個 arg ,這會打印出它的類型:
def some_method(self):
for f in inspect.getouterframes(inspect.currentframe() ):
args, _,_, local_dict = inspect.getargvalues(f[0])
if args:
first_arg = args[0]
first_value = local_dict[first_arg]
print(type(first_value).__name__)
你好,原諒死靈; 但這些都不適合我,所以受到這里的一些片段的啟發,我做了一個替代方案。 這就是我如何在 python 3.7 中為類方法做到的:
import inspect;
def QLNAME(frame):
code = frame.f_code;
name = code.co_name;
qual = None;
for cls in ( obj for obj in frame.f_globals.values() #
if inspect.isclass(obj) ):
if hasattr(cls, name):
member = getattr(cls, name);
if not inspect.isfunction(member): continue;
if member.__code__ == code:
qual = member.__qualname__;
break;
return qual;
它的工作方式實際上很簡單,我們檢查
如果 Nº4 為真,那么我們就知道cls
是一個具有某些方法member
的類,其代碼與我們從框架中獲得的代碼相同。
一切都很好,但這並不完美。 就我測試過的而言,它不適用於屬性修飾的方法,而且我還不確定如何處理這種極端情況。 但它在常規和靜態方法上對我有用,這是我目前所需要的,所以足夠公平。
但是請注意,這非常慢,盡管我認為這是不言而喻的。 根據我自己的基准測試,每 100,000 個調用通常需要大約 2 到 3 秒。 這對於我當前的用例來說是可以接受的,但是如果您需要一次多次使用它,您可能需要先進行一些優化。 我會把它交給你能干的人(:
希望搜索引擎在這里絆倒的其他人可以找到一些有用的東西。
干杯。
另一個死靈,但這次有一個明確的答案,具有完全想要的結果。
這個想法是利用 GC 中保存的對 function 的引用。 底線:使用下面定義的函數,返回值是調用 class:
import inspect
def get_type_of_calling_func():
frame = inspect.currentframe().f_back
func = get_func_from_frame(frame)
return get_class_from_func(func)
class Class:
def f(self):
return get_type_of_calling_func()
print(Class().f())
# <class '__main__.Class'>
下面在兩個實用函數中給出了主要功能(划分是為了可用性)。 這些函數的定義是:
import gc
from types import FunctionType, MethodType, FrameType
from typing import Optional, Union, Iterator, Mapping
def get_func_from_frame(frame: FrameType) -> Optional[FunctionType]:
# Nifty trick - get the function from the reference to its code object
refs = gc.get_referrers(frame.f_code)
for ref in refs:
if isinstance(ref, FunctionType):
return ref
return None
def get_class_from_func(func: Union[FunctionType, classmethod, MethodType]) -> Optional[type]:
cls_qual_name, func_name = func.__qualname__.rsplit('.', 1)
assert func_name == func.__name__
# If classmethod or method
if not isinstance(func, FunctionType):
self = getattr(func, '__self__', None)
for possibility in (self, type(self)):
if isinstance(possibility, type) and possibility.__qualname__ == cls_qual_name:
return possibility
# Recursively check GC references
visited_ids = set()
items_to_check = [func]
# The function is referenced within the type's dict, or its descriptor
while len(items_to_check) > 0:
obj = items_to_check[0]
items_to_check = items_to_check[1:]
obj_id = id(obj)
if obj_id in visited_ids:
continue
visited_ids.add(obj_id)
refs = gc.get_referrers(obj)
for ref in refs:
if isinstance(ref, type) and ref.__qualname__ == cls_qual_name:
return ref
if (
hasattr(ref, '__get__') or
# Then this is a descriptor (e.g. `classmethod`). Check if this works with the descriptor
isinstance(ref, Mapping)
# Then this is possibly a `__dict__` of a type
):
items_to_check.append(ref)
return None
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.