Python 元类这样用会产生问题吗? - V2EX
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 2956 次点击
    这是一个创建于 2300 天前的主题,其中的信息可能已经有所发展或是发生改变。

    元类

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

    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     Solana     5397 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 08:27 PVG 16:27 LAX 01:27 JFK 04:27
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86