V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
caneman
V2EX  ›  Python

Python 元类这样用会产生问题吗?

  •  
  •   caneman · 2019-06-29 16:43:21 +08:00 · 2610 次点击
    这是一个创建于 1976 天前的主题,其中的信息可能已经有所发展或是发生改变。

    元类

    我想通过元类做到以下几点:

    1.获取基于它产生的类的,具有特定命名规则的函数列表

    2.列表里面的函数可以直接被调用,而无需做多余额外的操作

    关键是第二点该如何操作? 例如列表 lst=[func1, func2],我想实现的是可以直接通过func1()func2()这种方式来运行函数

    我目前能做到的解决方案是

    class Metaclass(type):
    
        func_list = []
    
        def __new__(cls, name, base, attrs):
            obj = type.__new__(cls, name, base, attrs)()
            for k,v in attrs.items():
                if k.startswith('at'):
                    func = obj.__getattribute__(k)
                    Metaclass.func_list.append(func)
            return type.__new__(cls, name, base, attrs)
    
    
    class A(object, metaclass=Metaclass):
    
        def at_func1(self):
            print('A_func1')
    
        def at_func2(self):
            print('A_func2')
    

    请问这是最优的吗?或者一般的解决方法是什么?

    有没有办法在元类里面不创建 obj 的情况下,实现我上面的需求? 即obj = type.__new__(cls, name, base, attrs)()没有这行代码 因为 obj 并不会随着元类创建类的完毕而销毁,会有一个元类创建的实例对象( obj )常驻内存,而这个实例有太多额外的东西是不需要的,我仅仅需要的是里面的两个特定函数at_func1at_func2

    如果我把下面的at_func1(self)里面的 self 删掉,确实可以实现,不创建实例的情况下完成上面的需求

    但是这样导致的另一个结果就是,我如果自己在其他地方新建一个 a=A()的实例,那么执行a.at_func1()就会报错,有没有办法能权衡这两点?

    满足需求的情况下,

    1.在元类里面无需创建 obj

    2.在元类外面A().at_func1()能正常运行

    9 条回复    2019-07-01 14:33:27 +08:00
    NullPoint
        1
    NullPoint  
       2019-06-29 18:32:40 +08:00 via Android
    把两个方法变成静态方法就可以了吧,都不需要元类
    BingoXuan
        2
    BingoXuan  
       2019-06-29 18:56:34 +08:00 via Android
    你是想把一个多个符合特定格式的实例方法加到一个类里面吗?

    那直接构建一个类,其中一个类方法是通过 getattr 获取实例的方法,并加入类属性里面。如果实例方法的 self 要指定,那么用 function tool 的 partial 指定一个对象作为 self 传进去就好了。
    Dilras
        3
    Dilras  
       2019-06-29 20:15:34 +08:00
    不需要创建 obj,atts 字典里面本身就存着函数对象,所以直接 append 就好了
    ```python
    class Metaclass(type):

    func_list = []

    def __new__(mcs, name, base, attrs):
    for k,v in attrs.items():
    if k.startswith('at'):
    mcs.func_list.append(v)
    return type.__new__(mcs, name, base, attrs)
    ```
    caneman
        4
    caneman  
    OP
       2019-06-29 21:07:14 +08:00
    @NullPoint 方法和类后续都会扩展,直接用静态方法的话,没有办法维持一个满足给定条件的全部函数集合。

    @BingoXuan 我想实现的大概是这样一种场景,我创建一个元类,和若干子类,子类可能随时会扩展,但是我需要的仅仅是子类中满足特定格式命名的方法(实例方法、类方法、静态方法等都可以),我想在元类里面创建一个列表,里面是所有满足条件的方法的集合,如:func_list = [fun1, fun2, fun3],我想使这个列表中的元素(即每一个函数)可以直接执行,如:fun1()、func2(),而不需要其余多余的操作。

    @Dilras attrs 中 v 确实存的是函数的引用,但是是无法通过直接在其后面加括号运行的( func1())这种方法运行的,会提示缺少(self)参数,如果改成将 func1 改成类方法就会提示缺少(cls),如果什么都不填( def func1():return 'xx'),那么我在其他地方实例化这个类,A().func1()就无法运行了...,( A.func1()也不行)

    我的问题实质可能是,能否把一个类通过某些手段转换成一个变量+函数的集合,不具有其他多余的东西(类似于闭包的感觉),同时在其他情况下能够保留类本身的特性。
    lrxiao
        5
    lrxiao  
       2019-06-29 21:24:25 +08:00
    没搞懂 那不同的 self 怎么区分? 除非你这个类是单例
    BingoXuan
        6
    BingoXuan  
       2019-06-30 18:13:52 +08:00
    @caneman
    “”“我的问题实质可能是,能否把一个类通过某些手段转换成一个变量+函数的集合,不具有其他多余的东西(类似于闭包的感觉),同时在其他情况下能够保留类本身的特性。”“”

    这个设计有点不明所以,连你自己都不确定自己要什么,能实现什么功能,方便解决什么问题。总感觉你是实现类似 go 的 duck typing 风格的类。但我觉得如果一个类解耦成函数和变量的集合。应该通过类的多继承实现 mixin。通过多继承数据类和方法类,最终自由的组合任意数据类型和函数,实现高度解耦。甚至代理模式也可以。通过 override 代理类的__getattribute__方法,代理特定数据对象和方法对象的所有属性和方法。你甚至可以在__getattribute__这个方法里面通过访问 globals 获取当前所有定义的类的方法或者函数,再把数据扔进去执行。但这种 trick 我觉得都是苦劳。

    普通的继承也可以把所有子类的对象捆绑到最终的父类对象上。在子类实例化时候,把子类的所有符合条件方法绑定到父类属性里面(下面代码我默认全部公开的实例方法绑定),你可能需要修改一下判断条件。对静态函数,方法,类函数等,可能需要用 functiontool 这个库进行修改参数,比如去掉 self,指定默认 self 等等。

    class A:
    subclass_list = []
    def __init__(self):
    for attr in dir(self):
    if not attr.startswith('__') and isinstance(getattr(self,attr),MethodType):
    self.__class__.subclass_list.append(getattr(self,attr))
    frostming
        7
    frostming  
       2019-07-01 11:26:35 +08:00
    你为什么可以用一个 dummy 的实例去调用方法呢?那我要指定实例怎么办?
    如果调用方法跟 self 是什么没关系的话,你用 staticmethod 不就不用这个 obj 了吗?

    你的想法和你的解决方法互相矛盾,叫人如何回答
    caneman
        8
    caneman  
    OP
       2019-07-01 13:23:37 +08:00
    @BingoXuan 谢谢。

    我重新整理了下我的需求,
    1. 有很多功能模块,每个功能模块里面有若干方法,在主线程里面,我只需要用到这些模块的某些特定方法,不需要实例化这些功能模块,仅仅调用方法。(这种情况下,每个模块类似于变量+方法的集合)。

    2.在某些子线程里面,可能会实例化某一个功能模块(这种情况下,模块就是一般的类)。

    3.另一个需要满足的是,我需要有一个功能模块中心,它能够自动感知功能模块的增删,以及去所有功能模块里面收集并维持一个满足条件的方法列表。

    这样做的目的在于,我的主线程中的业务逻辑只需要关注功能模块中心即可,增删功能模块,不需要对其它地方做额外的变动。

    因为绝大多数的情况下,都是 1 在发挥作用,极少数情况下才会用到 2,所以我不希望在 1 的时候需要实例化每个功能模块,因为实例化带来的附加属性我是完全用不到,虽然也占不到几个内存。当时之所以采用元类的方式来解决,也是出于这一点,不需要实例化子类的情况下,便能完成子类中属性方法的获取。


    @frostming

    采用静态方法的化,我如何才能获得这个静态方法的引用呢?即 func1 这个变量如何才能指向 A.func1(),以便我 func1()便能 A.func1(),__getattr__不是实例方法吗,元类里面 attrs 里面存的函数引用,对于静态方法也是不能直接执行。
    for k,v in attrs.items(): v()这样是不行的


    现在的情况,我基本放弃 1 的需求了,实例就实例把,也占不了太多东西,这样的化解决方案就很多了,

    @BingoXuan @frostming 的方法都可以实现我的需求了。
    frostming
        9
    frostming  
       2019-07-01 14:33:27 +08:00
    @caneman 改成 staticmethod 以后你肯定不需要那个无用的 obj 了啊,用类本身就可以

    func = getattr(cls, name)

    1. 这类东西放在__init__里面而不是__new__里面
    2. 用 getattr(cls, name),不要直接用 cls.__getattribute__
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3185 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 13:35 · PVG 21:35 · LAX 05:35 · JFK 08:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.