Python常见面试题017: Python中是否可以获取类的所有实例

python,常见,面试题,是否,可以,获取,所有,实例 · 浏览次数 : 82

小编点评

答案是:可以。 虽然 Python 不会记录类(或内置类型)的实例,但可以使用 `weakref` 模块来跟踪类实例的引用。`weakref` 模块创建一个指向对象的所有弱引用列表,并可以通过 `__del__` 方法手动释放这些弱引用。 以下是使用 `weakref` 模块获取类实例的示例: ```python from weakref import proxy class A: instances = [] def __init__(self): self.name = stack()[1].code_context[0].split('=')[0].strip() self.instances.append(name) def func(): a2 = A() print(A.instances) ``` 这段代码将打印以下输出: ``` ['a', 'b'] ``` 这表明,`A` 类实例的 `instances` 列表包含两个实例:`a` 和 `b`。

正文

017. Python中是否可以获取类的所有实例

转载请注明出处,https://www.cnblogs.com/wuxianfeng023

出处

 https://docs.python.org/zh-cn/3.9/faq/programming.html#faq-multidimensional-list

  • 官方回答:Python 不会记录类(或内置类型)的实例。可以在类的构造函数中编写代码,通过保留每个实例的弱引用列表来跟踪所有实例

  • 所以答案是不可以?可以?

  • 准确的说是python不提供这样的接口(没做好)给你,但你要自己实现是么有问题的。

实现代码

  • 方式一

    class A:
        instances = []
        def __init__(self,name):
            self.name = name
            self.__class__.instances.append(self)
    class B:
        instances = {}
        def __init__(self,name):
            self.__class__.instances[self] = name
    
    a1 = A('a1')
    a2 = A('a2')
    print(A.instances) # [<__main__.A object at 0x00000250F285FAC0>, <__main__.A object at 0x00000250F285F7F0>]
    print(A.instances[0].name) # a1
    
    b1 = B('b1')
    b2 = B('b2')
    print(B.instances) # {<__main__.B object at 0x00000250F285F0A0>: 'b1', <__main__.B object at 0x00000250F285FEE0>: 'b2'}
    

  • 方式一有点问题

  • 比如你来个c = A('c1'),你处理的是实例化传递的c1,如果 你要获取c这个变量名是做不到的

  • 方式二

    from inspect import stack
    class A:
        instances = []
    
        def __init__(self):
            name = stack()[1].code_context[0].split('=')[0].strip()
            self.instances.append(name)
    
    a = A()
    b = A()
    print(A.instances)  # ['a','b']
    
  • 你会发现关键是stack(),而这是inspect中的

  • 详细你可以参考:https://docs.python.org/zh-cn/3.9/library/inspect.html?highlight=inspect

  • inspect.stack()

    inspect.stack(context=1)
    返回调用者的栈的帧记录列表。第一个记录代表调用者,最后一个记录代表了栈上最外层的调用。
    
    在 3.5 版更改: 返回一个 具名元组 FrameInfo(frame, filename, lineno, function, code_context, index) 的列表。
    
    具名元组:named tuple 可以参考 https://docs.python.org/zh-cn/3.9/glossary.html#term-named-tuple
    
    

  • 方式二一样有问题

  • 比如你对实例进行了del操作(这可能是显式的,也可能是隐式的),那你的处理是有问题的

    from inspect import stack
    class A:
        instances = []
    
        def __init__(self):
            name = stack()[1].code_context[0].split('=')[0].strip()
            self.instances.append(name)
    
    a = A()
    del a
    print(A.instances)  # ['a']  # 没错你仍然能得到这个a
    
  • 回到最开始的官方回答:通过保留每个实例的弱引用列表来跟踪所有实例,弱引用是啥?

  • 你可能要去看下官网

    https://docs.python.org/zh-cn/3.9/library/weakref.html
    
  • 简而言之是:对对象的弱引用不能保证对象存活:当对象的引用只剩弱引用时, garbage collection 可以销毁引用并将其内存重用于其他内容。但是,在实际销毁对象之前,即使没有强引用,弱引用也一直能返回该对象。术语 referent 表示由弱引用引用的对象。

  • 实现弱引用的模块是weakref

  • weakref返回一个类似其他语言指针的东西,在不影响python内建gc垃圾收集的情况下,创建一个指向该instance的弱引用。你可以理解python的gc机制类似于检测当前有没有任何引用该实例的对象,其中weakref就是创建一个新的引用,但这个引用在gc机制看来是“不存在”的,当只剩下weakref的时候gc就可以回收这块内存了

  • 方式三

    
    from inspect import stack
    from weakref import proxy
    
    class A:
        instances = []
    
        def __init__(self):
            self.name = stack()[1].code_context[0].split('=')[0].strip()
            self.instances.append((self.name,proxy(self)))
        def __del__(self):
            try:
                for name_instance in self.instances:
                    if name_instance[1] == self:
                        A.instances.remove(name_instance)
            except ReferenceError:
                print('引用被删了')
            #     for name_instance in self.instances:
            #         if name_instance[1] == self:
            #             A.instances.remove(name_instance)
            # ReferenceError: weakly-referenced object no longer exists
    a1 = A()
    del a1 # 显式的删除 ,但del是不一定会触发__del__的,因为可能还有别的引用
    def func():
        a2 = A()  # a2 也是一个实例,但它出了这个函数也不会存在
    func()
    a3 = A()
    print(A.instances)  # 只有a3
    # [('a3', <weakproxy at 0x0000013C5A7A43B0 to A at 0x0000013C5A5FF760>)]
    
    
    
    

拓展

  • 在stackoverflow上很早就有人问过类似的问题

    https://stackoverflow.com/questions/328851/printing-all-instances-of-a-class
    https://stackoverflow.com/questions/54000173/how-to-get-all-instances-of-a-class
    
  • 有一些有趣的代码你可以看下(稍作更改)

    def get_obj_instance_nums(dest_obj):
        '''
        获取对象的个数
        '''
        import gc
        obj_instance = []
        for obj in gc.get_objects():
            if isinstance(obj, dest_obj):
                obj_instance.append(obj)
        return len(obj_instance)
    
    class A:
        pass
    a1 = A()
    a2 = A()
    del a1  # 你如果删了,那返回1,不删就返回2
    print(get_obj_instance_nums(A)) # 1
    
  • 弱引用的另外一个示例

    from collections import defaultdict
    import weakref
    
    class KeepRefs(object):
        __refs__ = defaultdict(list)
        def __init__(self):
            self.__refs__[self.__class__].append(weakref.ref(self))
    
        @classmethod
        def get_instances(cls):
            for inst_ref in cls.__refs__[cls]:
                inst = inst_ref()
                if inst is not None:
                    yield inst
    
    class X(KeepRefs):
        def __init__(self, name):
            super(X, self).__init__()
            self.name = name
    
    x = X("x")
    y = X("y")
    for r in X.get_instances():
        print r.name
    del y
    for r in X.get_instances():
        print r.name
    

与Python常见面试题017: Python中是否可以获取类的所有实例相似的内容:

Python常见面试题017: Python中是否可以获取类的所有实例

017. Python中是否可以获取类的所有实例 转载请注明出处,https://www.cnblogs.com/wuxianfeng023 出处 https://docs.python.org/zh-cn/3.9/faq/programming.html#faq-multidimensional-

Python常见面试题001-005,涉及深浅拷贝、MRO、函数可变参数、作用域、is和==的区别等

Python常见面试题001-005 参考资料 https://github.com/taizilongxu/interview_python https://github.com/hantmac/Python-Interview-Customs-Collection https://github.

Python常见面试题006 类方法、类实例方法、静态方法有何区别?

006. Python中类方法、类实例方法、静态方法有何区别? 全部放一个里面篇幅过大了,就拆分成1个个发布 示例代码 class Human: def __init__(self, name): self.name = name def say(self): print(f'我的名字是{self.

Python常见面试题007. 谈谈Python中__init__和__new__的区别

007. 谈谈Python中__init__和__new__的区别 python中关于dunder method双下方法,或magic method魔术方法的描述多在 https://docs.python.org/zh-cn/3.9/reference/datamodel.html#special

Python常见面试题008. 谈谈python中的解包

008. 谈谈python中的解包 这是个简单的知识点,但有的学员并不理解 unpacking解包 解,对应的是*或者**,也有自动解包之说 包对应的可迭代对象 自动解包 赋值的demo a,b = [1,2] print(a) # 1 print(b) # 2 将容器里面的元素逐个取出来分别赋值

Python常见面试题011. 如何在Python中动态创建类?

011. 如何在Python中动态创建类? 说在前面 答案是type 你印象中的type是用来查看对象的类型的 li = [] type(li) # 得到list 对自定义的类是这样的 class Person: pass wuxianfeng = Person() type(wuxianfeng)

Python常见面试题009. 元组和列表有什么区别

009. 元组和列表有什么区别 这个题是简单的,但要拿满分或者说高分不容易 相同点 | 共性 | 说明 | | | | | 可以存放任意元素 | 一般都放同类型 | | 支持索引访问 | 甚至是负数 | | 支持切片操作 | | | 逗号分隔元素 | | | 都是有序集合(容器) | | | 可以随

Python常见面试题010. Python的int占多大内存?

010. Python的int占多大内存? 示例代码 import sys a = 0 print(a.__sizeof__()) # 24 print(sys.getsizeof(a)) # 24 所以答案是24?并不是!看下面 import sys b = 1 print(b.__sizeof_

Python常见面试题012. 可迭代对象和迭代器有啥区别?

012. 可迭代对象和迭代器有啥区别? 2者不是一回事(废话) 比如 from collections.abc import Iterable,Iterator print(isinstance([1, 2], Iterable)) # True 列表是可迭代对象 print(isinstance(

Python常见面试题013.请说出下面的代码返回结果是什么?

013.请说出下面的代码返回结果是什么? *的坑;简单题 参考:https://docs.python.org/zh-cn/3.9/library/stdtypes.html#typesseq 示例代码 lists = [[]] * 3 lists[0].append(1) 请问lists此时是什么