【问题标题】:Pythonic way of rewrite Java code in Python在 Python 中重写 Java 代码的 Pythonic 方式
【发布时间】:2012-06-28 06:19:46
【问题描述】:

我必须用 python 编写一个以前用 Java 编写的库。来自 Java 背景的 python 给我带来了一些困难。我坚持选择正确的 Pythonic 做事方式..

所以,我的 java 代码是这样的:

import java.util.Collection;

public abstract class MyEnumBaseClass
{
    protected int    value;
    protected String description = null;

    protected MyEnumBaseClass(int iValue, String iDescription)
    {
        value = iValue;
        description = iDescription;
    }

    public int getValue()
    {
        return value;
    }

    public String getDescription()
    {
        return description;
    }

    protected static MyEnumBaseClass getEnum(Collection<MyEnumBaseClass> iter, int value)
    {
        for (MyEnumBaseClass enumObj : iter)
        {
            if (enumObj.getValue() == value)
            {
                return enumObj;
            }
        }
        return null;
    }
}



import java.util.ArrayList;
import java.util.Collection;

public class MyEnumClass extends MyEnumBaseClass
{
    private final static Collection<MyEnumBaseClass> enumList   = new ArrayList<MyEnumBaseClass>();

    public final static int                          ERROR1     = 1;
    public final static int                          ERROR2     = 2;
    public final static int                          ERROR3     = 3;
    public final static int                          ERROR4     = 4;

    public final static MyEnumClass                  ErrorEnum1 = new MyEnumClass(ERROR1, "ERROR1");
    public final static MyEnumClass                  ErrorEnum2 = new MyEnumClass(ERROR2, "ERROR1");
    public final static MyEnumClass                  ErrorEnum3 = new MyEnumClass(ERROR3, "ERROR1");
    public final static MyEnumClass                  ErrorEnum4 = new MyEnumClass(ERROR4, "ERROR1");

    protected MyEnumClass(int iValue, String iDescription)
    {
        super(iValue, iDescription);
    }

    public static int getCount()
    {
        return enumList.size();
    }

    public static Collection<MyEnumBaseClass> getList()
    {
        return enumList;
    }

    public static MyEnumBaseClass getEnum(int value)
    {
        return getEnum(enumList, value);
    }
}

我想用python写一些东西。我理解两种语言完全不同。我不想复制确切的代码,但我想在 python 中写一些东西来给我 Java 代码所提供的功能。

所以我想出了类似的东西:

# MODULE MYENUMBASECLASS:::

class MyEnumBaseClass(object):

    def __init__(self, iValue, iDescription, ui = None):
        self._value = iValue
        self._description = iDescription

    def getValue(self):
        return self._value

    def getDescription(self):
        return self._description

    @classmethod
    def getEnum(cls, value, itr):
        for enumObj in itr:
            if enumObj.getValue() == value:
                return enumObj
        return None


# MODULE: ENUMS:::
from MyEnumBaseClass import MyEnumBaseClass

__all__ = ["MyEnumClassConstants", "MyEnumClass", "MyEnums"]
_enumList = []

class MyEnumClassConstants(object):
    ERROR1 = 1
    ERROR2 = 2
    ERROR3 = 3
    ERROR4 = 4

class MyEnumClass(MyEnumBaseClass):
    def __init__(self, v, d, ui):
        global _enumList
        super(MyEnumClass, self).__init__(v, d, ui)
        _enumList.append(self)

    @staticmethod
    def getCount():
        return len(_enumList)

    @staticmethod
    def getList():
        return _enumList

    @classmethod
    def getEmum(cls, value, itr = None):
        return super(MyEnumClass, cls).getEnum(value, _enumList)


class MyEnums(object):
    ErrorEnum1 = MyEnumClass(MyEnumClassConstants.ERROR1, "ERROR1");
    ErrorEnum2 = MyEnumClass(MyEnumClassConstants.ERROR2, "ERROR2");
    ErrorEnum3 = MyEnumClass(MyEnumClassConstants.ERROR3, "ERROR3");
    ErrorEnum4 = MyEnumClass(MyEnumClassConstants.ERROR4, "ERROR4");

我想知道:

  1. 这是正确的pythonic方式吗?

  2. 我想将 ErrorEnum1,2,3,4 和常量作为模块变量从 MyEnums 类中移出。但这样我的 all 变量中就会有一个很长的列表。当我在其他模块中导入 Enums 模块时,我也有变量名冲突的风险(其他一些 Enums2 模块也可能有 ErrorEnum1,2,3.. 但这不是一个大问题。我们总是可以使用 Enums.ErrorEnum1 和 Enums2 .ErrorEnum1)。我想对了吗?

  3. 我知道这并不完美(我的第一个 python 代码)。所以我邀请你们给我一些想法。

谢谢

【问题讨论】:

  • 完全跑题了,但是你有没有使用 Java 的枚举类型的原因?
  • 对此我没有任何解释。因为当我得到这个创建python API的工作时,它已经是这样了。这是在 SOAP 消息中使用的。因此,据我了解,我粘贴的 java 代码具有错误代码和描述的优势。虽然 Java 枚举没有。我还删除了其他代码,因为这与我在这里提出的问题无关。
  • Java 中的枚举实际上只是特殊的类,因此您可以将几乎所有可以附加到普通类的东西附加到它上面。题外话,因为这个问题是关于 Python 代码,而不是 Java 代码,但我只是想知道...... ;)
  • 我的眼睛!我的眼睛! :((顺便说一句,阅读this
  • @brandizzi 我期待这个回复这么长的帖子..不错的链接顺便说一句。 :)

标签: java python error-code


【解决方案1】:

好吧,我想你知道你的代码不是最优的,当然不是要走的路。另一个问题是我们不能说你怎么能“用python写一些东西给我Java代码提供的功能”,因为我们不知道你到底想做什么。也就是说,您的代码中有很多明显的 Java 偏见,可以毫无问题地消除:

  • 首先,为什么要有MyEnumBaseClassMyEnumClass?你只能拥有一个。它将减少类的数量和模块的数量。即使你想扩展你的枚举,你也会看到,在简化你的代码之后,你的MyEnumClass 会非常简单,你可以毫无问题地扩展它。

  • 现在,请不要使用 getter 和 setter。你没有理由使用它们,因为你有属性。如果您的属性只会获取和设置值,请不要使用属性:

    class MyEnumClass(object):
        def __init__(self, value, description, ui = None):
            self.value = value
            self.description = description
    
  • 创建一个类只是为了保存类似常量的值,例如MyEnumClassConstantsMyEnums,真的很丑陋。只需在模块级别创建一些变量。让我们在下面的项目之后看看如何做到这一点。

  • 另外,为什么getEnum() 是一个类方法?这可以只是一个函数,您不必担心冲突,因为它位于模块内部:

    def getEnum(value, itr):
        for enumObj in itr:
            if enumObj.value == value:
                return enumObj
        return None
    
  • 如果您要设置一些带有序号的变量,您可能需要使用unpacking idiom

    (
        ERROR1,
        ERROR2,
        ERROR3,
        ERROR4
    ) = range(1, 5)
    
  • 这个成语也可以用来创建你的枚举列表:

    _enums = (
        ErrorEnum1,
        ErrorEnum2,
        ErrorEnum3,
        ErrorEnum4
    ) = (
        MyEnumClass(ERROR1, "ERROR1"),
        MyEnumClass(ERROR2, "ERROR2"),
        MyEnumClass(ERROR3, "ERROR3"),
        MyEnumClass(ERROR4, "ERROR4")
    )
    
  • 老实说,我很乐意将 _enums 作为模块的公共成员,但让我们轻松处理您的 Java-itis :P 就像我们处理 getEnum() 一样,让我们​​处理其他类方法:将它们声明为模块中的函数:

    def getCount():
        return len(_enums)
    
    def getList():
        return _enums
    
  • 我们甚至可以通过更改默认参数来改进getEnum()

    def getEnum(value, itr=_enums):
        for enumObj in itr:
            if enumObj.value == value:
                return enumObj
        return None
    
  • 我也很乐意在这里禁止 __all__ 声明。唯一不属于接口的是_enums 元组,它前面是_,根据PEP-8,这意味着它不应该在外部使用。但让我们说它会留下来。你的模块有一个新的接口,有更多的常量和函数:

    __all__ = ["MyEnumClass", "ERROR1", "ERROR2", "ERROR3", "ERROR4",
            "ErrorEnum1", "ErrorEnum2", "ErrorEnum3", "ErrorEnum4",
            "getCount", "getList", "getEnum"]
    

    似乎最好从界面中删除MyEnumClass,但好吧,你可能想使用它,所以我会离开它。请注意,__all__并不能避免访问模块的其他组件。它只是更改了文档

最终结果会是这样的:

__all__ = ["MyEnumClass", "ERROR1", "ERROR2", "ERROR3", "ERROR4",
        "ErrorEnum1", "ErrorEnum2", "ErrorEnum3", "ErrorEnum4",
        "getCount", "getList", "getEnum"]

class MyEnumClass(object):
    def __init__(self, value, description, ui = None):
        self.value = value
        self.description = description

(
    ERROR1,
    ERROR2,
    ERROR3,
    ERROR4
) = range(1, 5)


_enums = (
    ErrorEnum1,
    ErrorEnum2,
    ErrorEnum3,
    ErrorEnum4
) = (
    MyEnumClass(ERROR1, "ERROR1"),
    MyEnumClass(ERROR2, "ERROR2"),
    MyEnumClass(ERROR3, "ERROR3"),
    MyEnumClass(ERROR4, "ERROR4")
)

def getCount():
    return len(_enums)

def getList():
    return _enums

def getEnum(value, itr=_enums):
    for enumObj in itr:
        if enumObj.value == value:
            return enumObj
    return None

(已编辑) 这真的不是很简单。如果我要创建错误代码,我只会创建ERROR1ERROR2 等变量——没有类、没有函数,只有变量中的值。实际上,即使创建错误代码的想法似乎也不合适:您应该更喜欢创建异常,因为正如 Python 之禅所说,errors should never pass silently(或者,正如 Eric Raymond 的 Unix 哲学所说,when you must fail, fail noisily and as soon as possible)。尽管如此,我敢打赌,我所做的更改可以让您对编写 Python 有更准确的了解。

您可能对以这种方式做事感到很痒,但相信我,这是最好的方式。有些人可能在某些方面不同意我的观点,但这个想法是提出的。我主要是一名 Java 开发人员,但跟随语言的音乐起舞很重要 - 不要试图将外国概念强加于其中。

最后,一些重要的参考资料:

  • PEP-20 - Python 的禅宗:用 Python 和 Python 进行开发的核心价值观有点诗意。
  • PEP-8 - Python 的风格指南。 立即阅读
  • Python is not Java - 关于在 Python 中避免不当 Java 习惯的最佳指南之一。

【讨论】:

  • 我添加了另一个参考并编辑了最后一段,添加了一些要点。
  • 这是肥皂处理错误。异常在调用堆栈中向上一级抛出。这只是表示来自 Soap 消息解析的错误状态。
  • @Ashutosh 好吧,现在更有意义了 :)
  • 还有一个问题。这些类通常应该如何在 python 中进行分类?就像在java中一样,我们有packeges,然后每个文件都有一个类来编写代码。但是python有可以包含多个类的模块。所以: 1. 我应该每个模块写一个类吗? 2. 是否应该在一个模块中合并类,即使它们彼此不相关? 3. 只有当我有相似和相关的课程时才应该合并?我觉得 3 是更正确的方式,但我的 java 类的方式是,我通常会在每个模块中结束一个类,或者在 python 中的一个模块中包含 10-20 个类......
  • @Ashutosh 实际上,第三种解决方案是最常见的选择:将相关的类(和函数)分组。这是没有通用标准的地方。您应该遵循您正在处理的项目的默认结构,或者使用常识,平衡相关组件的内聚性和文件的大小。
【解决方案2】:
  • 一般来说,Python 类属性是公开的,除非有非常好的理由不公开。

  • 如果属性必须是私有的,那么使用@property 装饰器比使用getProperty 方法更符合Pythonic。

.

class MyEnumBaseClass(object):
    def __init__(self, value, description, ui=None):
        self.value = value
        self._description = description
        self.ui = [] if ui is None else ui

    @property
    def description(self):
        return self._description

    @classmethod
    def get_enum(cls, value, itr):
        for enumObj in itr:
            if enumObj.value == value:
                return enumObj
        return None

【讨论】:

    【解决方案3】:

    更pythonic的方法:

    class EnumValue(object):
        def __init__(self, value, description):
            self.value = value
            self.description = description
    
    class EnumType(object):
        @classmethod
        def lookup(cls, value):
            for error in cls.VALUES:
                if error.value == value:
                    return value
            raise ValueError(value)    
    
    class MyErrors(EnumType):
        ERROR1 = EnumValue(1, "Simple Errors")
        ERROR2 = EnumValue(2, "Bigger Errors")
        ERROR3 = EnumValue(3, "Really Big Errors")
    
        VALUES = [ERROR1, ERROR2, ERROR3]
    

    【讨论】:

    • 我需要将 MyEnumBaseClass 分开,因为还有其他数以千计的 errorEnumClass,例如扩展 MyEnumBaseClass 的 MyEnumClass。所以,我不能将查找(getEnum)方法移动到 MyErrors(枚举)。我还需要常量,以便我可以在其他模块/类中比较或使用它们。我希望这有意义并解释限制!
    • @Ashutosh,数千?哎呀!将公共元素拉入基类很容易(见编辑)。您可以使用MyErrors.ERROR1.value 访问常量,但需要它们是可疑的。您应该始终是 EnumValue 对象或始终使用整数。选择一个并坚持下去。
    • 我刚刚意识到我可能将错误类别的数量乘以 10 或 20 倍。:)
    猜你喜欢
    • 2015-04-21
    • 2013-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多