假设我在alotoffunc.py
中有很多函数被多于一种类型的对象使用。
我们说ObjectI
和ObjectII
以及ObjectXI
都使用alotoffunc.py
中的某些功能。每个Object都使用不同的函数集,但所有对象都有变量object.table
。
alotoffunc.py
:
def abc(obj, x):
return obj.table(x) * 2
def efg(obj, x):
return obj.table(x) * obj.table(x)
def hij(obj, x, y):
return obj.table(x) * obj.table(y)
def klm(obj, x, y):
return obj.table(x) *2 - obj.table(y)
然后我导入函数并重载它们:
import alotoffunc
class ObjectI:
def abc(self, x):
return alotoffunc.abc(self, x)
def efg(self, x):
return alotoffunc.efg(self, x)
class ObjectII:
def efg(self, x):
return alotoffunc.efg(self, x)
def klm(self, x, y):
return alotoffunc.klm(self, x, y)
class ObjectXI:
def abc(self, x):
return alotoffunc.abc(self, x)
def klm(self, x, y):
return alotoffunc.klm(self, x, y)
现在看起来像一个大混乱,我应该如何构建我的对象类并安排我的alotoffunc.py
?
答案 0 :(得分:6)
(1)您可以拥有一个实现所有方法的基类,然后覆盖不必要的方法,以在子类中引发NotImplementedError
。
(2)你可以使用mixins来减少重复:
import alotoffunc
class MixinAbc:
def abc(self, x):
return alotoffunc.abc(self, x)
class MixinEfg:
def efg(self, x):
return alotoffunc.efg(self, x)
class MixinKlm:
def klm(self, x, y):
return alotoffunc.klm(self, x, y)
class ObjectI(MixinAbc, MixinEfg):
pass
class ObjectII(MixinEfg, MixinKlm):
pass
class ObjectXI(MixinAbc, MixinKlm):
pass
您也可以将此方法与@cpburnz的方法结合使用。
答案 1 :(得分:5)
最简单的方法是将所需的函数作为实例方法直接绑定到其定义中的类。请注意,每个函数都将接收self
作为第一个参数。
import alotoffunc
class ObjectI:
abc = alotoffunc.abc
efg = alotoffunc.efg
class ObjectII:
efg = alotoffunc.efg
klm = alotoffunc.klm
class ObjectXI:
abc = alotoffunc.abc
klm = alotoffunc.klm
如果没有针对各种功能的明确的逻辑分组,这可以是一种更简单的方法来定义混合类。分组确实取决于您的用例,因此混合方法可能会更好,具体取决于具体情况。
答案 2 :(得分:5)
如果我想避免使用mixins,也许是为了最小化代码的不透明度,我会这样做:
class ObjectI:
from alotoffunc import abc, efg
class ObjectII:
from alotoffunc import efg, klm
class ObjectXI:
from alotoffunc import abc, klm
每当您创建类的实例时,导入的方法都会自动绑定。换句话说,默认情况下它们是实例方法。
如果您希望它们是静态方法,请使用staticmethod
,如下所示:
class ObjectI:
from alotoffunc import abc, efg
abc = staticmethod(abc)
此外,我不会过多担心因使用这些多个import语句而导致的性能问题,因为Python非常智能,只能运行一次导入的模块,然后将其保存在内存中,以备日后需要时使用。
如果您要导入的函数具有某种逻辑分组,那么您绝对应该使用mixins,或者将函数组织到单独的“mixin模块”中,这样您甚至可以执行from mixinmodule import *
。对于mixin级方法,我认为常规import语句比from-import更好,除非你的函数名很长,你只想输入一次!
答案 3 :(得分:3)
你可以这样做:
from alotoffunc import *
然后,如果 - 例如 - 我们有变量o1
,o2
,它们是Object1
和Object2
的实例,我们可以这样做:
abc(o1,x)
efg(o2,x)
...
事实并非如此'漂亮'作为调用o1.abc(x)的OO方法,但它比必须覆盖所有方法更具可维护性。
from ___ import *
所做的是让您免除必须使用alotoffunc.abc
之类的包名称等。
正如@JamesKing所述,如果你的类中有一个具有相同名称的函数,则此解决方案不起作用。因为它们似乎无用 - 它们只是调用导入的函数 - 我正在考虑删除它们。如果这不适用于您的代码,则应使用from ___ import ___ as ___
,如他所说。
答案 4 :(得分:3)
这是一家"工厂"做法。如果你没有那么多功能,可能会过于复杂,但是如果你真的有很多"它可能会有用。
import alotoffunc
class Builder(object):
def __init__(self, objtype):
objtypes = { 'ObjectI': ['abc', 'efg'],
'ObjectII': ['efg', 'klm'],
'ObjectXI': ['abc', 'klm']}
for func in objtypes[objtype]:
self.__dict__[func] = getattr(alotoffunc, func)
答案 5 :(得分:3)
这个怎么样:
import alotoffunc
import types
class ObjectI:
def __init__(self):
setattr(self, abc, types.MethodType(alotoffunc.abc, self))
setattr(self, efg, types.MethodType(alotoffunc.efg, self))
class ObjectII:
def __init__(self):
setattr(self, efg, types.MethodType(alotoffunc.efg, self))
setattr(self, klm, types.MethodType(alotoffunc.klm, self))
class ObjectXI:
# ...
(可能存在一些漏洞或拼写错误,但基本想法是正确的。我在代码中使用类似的东西。
这会在创建时将原始函数绑定到每个实例。 setattr()不是必需的,但我更喜欢这个信号表明发生了一些"魔法"。
我非常确定有更好的方法在类对象中创建方法,但这可能需要元类和/或更多魔法。
答案 6 :(得分:1)
我认为实现目标的更简洁方法是使用python的多重继承功能。许多程序员讨厌MI,声称它产生了混乱的代码或者很难维护。只要您在计划课程时要小心,使用MI会非常有帮助。
One of the many links returned by Google
SomeClasses.py - 通常每个类都存储在一个单独的文件中。
class ABCClass:
def abc(obj, x):
return obj.table(x) * 2
class DEFClass:
def efg(obj, x):
return obj.table(x) * obj.table(x)
class HIJClass:
def hij(obj, x, y):
return obj.table(x) * obj.table(y)
class KLMClass:
def klm(obj, x, y):
return obj.table(x) *2 - obj.table(y)
TheProject.py - 除非你真的需要覆盖基类的功能,否则不需要额外的代码。
import SomeClasses #or each class file
class ObjectI (ABCClass, EFGClass):
pass
class ObjectII (EFGClass, KLMClass):
pass
class ObjectXI (ABCClass, KLMClass):
pass
当然这是一个简单的例子,其中每个基类只有一个函数。想到的一个真实例子是将图像打包到共享纹理资源上。您需要PIL.Image的功能,但能够将图像作为叶节点存储在自制的树数据结构中。您可以使用多重继承来使用最少的代码获取这两个类的方法。 LeafImage将拥有这两个类的所有方法,当您决定修改TreeLeaf时,LeafImage中不需要额外的代码。
class LeafImage (PIL.Image, TreeLeaf):
pass