Python对象中的好风格 [英] Good Style in Python Objects

查看:111
本文介绍了Python对象中的好风格的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Python的大部分编程都是在C ++或Matlab中。我没有CS学位(几乎完成物理博士学位),但已经完成了一些课程和大量的实际编程。现在,我正在考察Coursera的课程(优秀的课程,顺便说一句,来自斯坦福大学的一名教授)。我决定在Python中实现功课。然而,有时我发现自己想要的东西,语言并不那么容易支持。我习惯于为C ++中的东西创建类和对象,以便将数据分组(即没有方法)。然而,在Python中,您可以随时添加字段,我基本上最终希望所有的时间都是Matlab结构体。我认为这可能是一个迹象,我没有使用好的风格和做Pythonic的方式。



下面是我实现一个union-find数据结构(对于Kruskal的算法)。虽然实施相对较短,工作良好(错误检查没有太多),但有一些奇点。例如,我的代码假定原来传入union-find的数据是一个对象列表。但是,如果显式数据的列表被代替传递(即int的列表),代码就会失败。有没有更清楚,更加Pythonic的方式来实现呢?我试图google这个,但大多数例子非常简单,并且更多地涉及程序代码(即在python中执行for循环的正确方式)。

  class UnionFind:
def __init __(self,data):
self.data = data

for d in self.data:
d.size = 1
d.leader = d
d.next =无
d.last = d

def find(self,element):
return element.leader

def union(self,leader1,leader2):
如果leader1.size> = leader2.size:
newleader = leader1
oldleader = leader2
else:
newleader = leader2
oldleader = leader1

newleader.size = leader1.size + leader2.size

d = oldleader
while d!= None:
d.leader = newleader
d = d.next

newleader.last.next = oldleader
newleader.last = oldleader.last

del(oldleader.size)
del(oldleader.last)


解决方案

一般来说,做这样的事情Pythonically意味着你试图让你的代码不在乎给予它什么,至少不再比真的需要。



让我们来看一下联合查找算法的具体例子。联合查找算法实际上与传递给它的值唯一的作用是比较它们的相等性。因此,为了使一般有用的 UnionFind 类,您的代码不应该依赖于它接收到的值与平等测试之外的任何行为。特别是,您不应该依赖于能够为值分配任意属性。



我建议的方法是让 UnionFind 使用包含给定值的包装器对象以及使算法正常工作所需的任何属性。您可以使用 namedtuple 由另一个答案建议的,或者做一个小包装类。当元素添加到 UnionFind 中时,首先将其包装到其中一个对象中,并使用包装器对象来存储属性 leader size 等等。只有您访问被包装的东西才能检查是否等于另一个值。



在实践中,至少在这种情况下,应该安全地假定您的值是可哈希的,以便您可以将它们用作Python字典中的键,以查找与给定的对应的包装器对象值。当然,并不是Python中的所有对象都必须可以是哈希的,但是那些不是的对象是相对罕见的,而且要做一个能够处理这些对象的数据结构将会变得更多。


Most of my programming prior to Python was in C++ or Matlab. I don't have a degree in CS (almost completed a PhD in physics), but have done some courses and a good amount of actual programming. Now, I'm taking an algorithms course on Coursera (excellent course, by the way, with a professor from Stanford). I decided to implement the homeworks in Python. However, sometimes I find myself wanting things the language does not so easily support. I'm very used to creating classes and objects for things in C++ just to group together data (i.e. when there are no methods). In Python however, where you can add fields on the fly, what I basically end up wanting all the time are Matlab structs. I think this is possibly a sign I am not using good style and doing things the "Pythonic" way.

Underneath is my implementation of a union-find data structure (for Kruskal's algorithm). Although the implementation is relatively short and works well (there isn't much error checking), there are a few odd points. For instance, my code assumes that the data originally passed in to the union-find is a list of objects. However, if a list of explicit pieces of data are passed in instead (i.e. a list of ints), the code fails. Is there some much clearer, more Pythonic way to implement this? I have tried to google this, but most examples are very simple and relate more to procedural code (i.e. the "proper" way to do a for loop in python).

class UnionFind:
    def __init__(self,data):
        self.data = data

        for d in self.data:
            d.size = 1
            d.leader = d
            d.next = None
            d.last = d

    def find(self,element):
        return element.leader

    def union(self,leader1,leader2):
        if leader1.size >= leader2.size:
            newleader = leader1
            oldleader = leader2
        else:
            newleader = leader2
            oldleader = leader1

        newleader.size = leader1.size + leader2.size

        d = oldleader
        while d != None:
            d.leader = newleader
            d = d.next

        newleader.last.next = oldleader
        newleader.last = oldleader.last

        del(oldleader.size)
        del(oldleader.last)    

解决方案

Generally speaking, doing this sort of thing Pythonically means that you try to make your code not care what is given to it, at least not any more than it really needs to.

Let's take your particular example of the union-find algorithm. The only thing that the union-find algorithm actually does with the values you pass to it is compare them for equality. So to make a generally useful UnionFind class, your code shouldn't rely on the values it receives having any behavior other than equality testing. In particular, you shouldn't rely on being able to assign arbitrary attributes to the values.

The way I would suggest getting around this is to have UnionFind use wrapper objects which hold the given values and any attributes you need to make the algorithm work. You can use namedtuple as suggested by another answer, or make a small wrapper class. When an element is added to the UnionFind, you first wrap it in one of these objects, and use the wrapper object to store the attributes leader, size, etc. The only time you access the thing being wrapped is to check whether it is equal to another value.

In practice, at least in this case, it should be safe to assume that your values are hashable, so that you can use them as keys in a Python dictionary to find the wrapper object corresponding to a given value. Of course, not all objects in Python are necessarily hashable, but those that are not are relatively rare and it's going to be a lot more work to make a data structure that is able to handle those.

这篇关于Python对象中的好风格的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆