下面的类(这是我在python中的第一个类)用于眼睛跟踪上下文。想象一下,在这样的背景下,你总是会有一个单一的注视点和一些封闭的轮廓。我怎样才能归还他们之间所有可能的关系?
例如,对于两个等高线,要点可以是:
在我的例子中,我使用每个关系作为颜色的标记,在轮廓数目变化的上下文中。
您也可以想象这些轮廓集,但让我们避免高级集合论的复杂性。
这个问题给了我们2的幂c=等高线数。一个算盘给我们的列数乘以幂i=线数。因此,算盘似乎是一个很好的比喻,例如:
contours = [c1, c2]
Abacus = ClassAbacus (len(contours))
color_tags = Abacus.Enumerate()
给我们一个列表,上面有所有可能的标签,其中有+在里面,-在外面,数字是等高线:
“+1+2”、+1-2“、”-1+2“、”-1-2“
所以,现在我们有了4种颜色的参考。我们可以给每一点取一个名字:
import cv2
...
def PolygonTestEx(contours, pt, contours_counter = 0, counter_code = ''):
for x in xrange(1, len(contours) + 1):
Inside = cv2.pointPolygonTest(contours[x -1], pt, False)
# Inside = contours[x]
if Inside > -1:
counter_code = counter_code + '+' + str(x + contours_counter)
else:
counter_code = counter_code + '-' + str(x + contours_counter)
contours_counter = contours_counter + len(contours)
return contours_counter, counter_code
在这方面,它应该给出四种可能的状态:
我能以更好的可读性写这门课吗?
拜托,可以建议一种更快的方法来做同样的事情,或者在OOP中错过一些常见的实践。
CURRENT_LINE = 0
SIGNS = 1
class BaseError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class ClassAbacus(object):
"""
Recombining tool.
Imagine each char in char_set as digits, or beads, and count them accordingly to a base reference.
Imagine the base as the abacus lines.
'Counter' returns an integer equals to len(char_set) raised to the power x = base, giving the number
of all possible bead states of the "abacus".
'Enumarate' prints each or all states.
"""
def __init__(
self,
char_set = ['+', '-'],
base = 2
):
super(ClassAbacus, self).__init__()
self.Chars = char_set
self.Counter = 0
self.States = []
self.Abacus = []
self.Base = base
if (base + 1) > 2:
self.__Base = base + 1
self.Instantiate()
else:
try:
raise BaseError(1)
except BaseError as e:
print 'Use argument "base" higher then one.', e.value
def doSaveState(self):
state = ''
for column in xrange(0, len(self.Abacus)):
# def addSignTo(state, column, item):
state = state + self.Abacus[column][SIGNS][ self.Abacus[column][CURRENT_LINE] ]
self.States.append(state)
def doLineReset(self, from_column):
to_column = len(self.Abacus)
for column in xrange(from_column, to_column):
self.Abacus[column][CURRENT_LINE] = 0
def doLineIncrement(self, column):
self.Counter += 1
self.doSaveState()
n = self.Abacus[column][CURRENT_LINE]
self.Abacus[column][CURRENT_LINE] = n + 1
def doCount(self, Column):
KeepsCounting = True
HighColumn = len(self.Abacus) -1
LowColumn = 0
if Column < LowColumn:
self.Counter += 1
self.doSaveState()
KeepsCounting = False
else:
ColumnMaxLine = len(self.Abacus[Column][SIGNS]) -1
ColumnCurLine = self.Abacus[Column][CURRENT_LINE]
if ColumnCurLine == ColumnMaxLine:
Column -= 1
KeepsCounting = self.doCount(Column)
return KeepsCounting
if ( Column < HighColumn ) and ( ColumnCurLine < ColumnMaxLine ):
self.doLineIncrement(Column)
self.doLineReset( Column + 1 )
self.doCount(HighColumn)
return KeepsCounting
if ( Column == HighColumn ) and ( ColumnCurLine < ColumnMaxLine ):
self.doLineIncrement(Column)
self.doCount(Column)
return KeepsCounting
def Instantiate(self):
# set the grid of the abacus
for x in xrange(1, self.__Base):
container = []
for char in self.Chars:
# concatenate each char with its base reference
container.append(char + str(x))
self.Abacus.append([0, container])
max_column = max(self.Abacus)
max_index = self.Abacus.index(max_column)
self.doCount(max_index)
def Enumerate(self, index = -1):
container = []
if (index < 0) or (index > self.Counter - 1):
for state in self.States:
container.append(state)
return container
else:
return self.States[index]
为了完整起见,这个类将被称为这里。
发布于 2015-01-03 08:44:08
我还没有安装cv2
,所以我在这里失明了,但我有一些评论。首先,您所描述的问题可以很容易地用itertools.product
解决:
import itertools
def get_set_intersections(chars="+-", base=2):
numbers = range(1, base+1)
for signs in itertools.product(chars, repeat=base):
yield "".join("{}{}".format(sign, n) for sign, n in zip(signs, numbers))
list(get_set_intersections())
#>>> ['+1+2', '+1-2', '-1+2', '-1-2']
第二,对代码的一些评论。
在长度为2的列表中使用CURRENT_LINE
和SIGNS
作为常量索引,而应该使用类。这里有一个:
class AbacusColumn(object):
def __init__(self, current_line, signs):
self.current_line = current_line
self.signs = signs
你上了一门新课,BaseError
。但是,您不应该在旧错误已经足够的地方创建新错误;只需使用ValueError
即可。即使您确实想要一个新类,它也应该是ValueError
的一个子类。
唯一一次使用BaseError
时:
try:
raise BaseError(1)
except BaseError as e:
print 'Use argument "base" higher then one.', e.value
...which等价于
print 'Use argument "base" higher then one. 1'
相反,做
raise ValueError('Use argument "base" higher then one.')
另外,切换条件,这将变成:
if (base + 1) <= 2
呃,你是说
if base <= 1
?
不要使用两个双下划线(self.__Base
)的属性。使用单个下划线。两个给你起了一个你不想要的名字。
函数定义行应格式化为
def __init__(self, char_set=['+', '-'], base=2):
因为它太短,不需要线包装。
属性应该在snake_case
中。
你从不使用self.Base
。把它移开。实际上,移除_Base
,因为您只需在使用点添加一个。
您不需要调用super
;您的超类是object
。
(在转到AbacusColumn
类之后)
state = ''
for column in xrange(0, len(self.abacus)):
state = state + self.abacus[column].signs[self.abacus[column].current_line]
首先,我们应该把这件事讨论一下。
state = ''
for column in self.abacus:
state += column.signs[column.current_line]
其次,在循环中添加不可变的容器是不好的。使用str.join
代替:
state = ''.join(column.signs[column.current_line] for column in self.abacus)
在do_line_reset
中,您应该做类似的事情
for column in self.abacus[from_column:]:
column.current_line = 0
do_line_increment
应该将+=
用于
self.abacus[column].current_line += 1
我有一种感觉,keeps_counting
在do_count
坏了。逻辑是:
keeps_counting = True
if ...:
keeps_counting = False
else:
if ...:
keeps_counting = self.do_count(column)
return keeps_counting
if ...:
return keeps_counting
if ...:
return keeps_counting
[function ends]
这可以简化为:
if ...:
...
else:
if ...:
return self.do_count(column)
if ...:
return True
if ...:
return True
这意味着在第一个分支中返回的是None
,而不是False
,或者如果三个if
s都失败了。但是,这是可以的,因为你从来不使用结果。把它移开。
在instantiate
中,可以使用列表理解简化生成self.abacus
:
self.abacus = [
AbacusColumn(0, [char + str(x+1) for char in self.chars])
for x in range(self.base)
]
您的max(self.abacus)
将不得不更改,因为AbacusColumn
是不可比拟的,但是它还是有缺陷的,因为它们都被初始化为[0, list of strings]
。由于字符串都是相同的表单,所以实际上只是找到了最大的索引(尽管因为要进行字符串比较,它的基数大于9)。就这么做
self.do_count(self.base - 1)
在enumerate
中,您需要:
for state in self.states:
container.append(state)
return container
这只是
return list(self.states)
你的比较最好写成
if not (0 < index < self.counter):
虽然我建议你把这个改成
if not (0 <= index < self.counter):
事实上,根据index
是否超出界限返回不同类型是愚蠢的,您应该一直返回list(self.states)
。如果您希望第二种方法只访问一种方法,请创建一个新方法。
你的名字ClassAbacus
很奇怪:当然Abacus
更好。
您只需从instantiate
从__init__
调用,所以我建议将它移到那里。如果不在函数中,则在函数下面。这样做允许您删除self.base
和self.chars
。
将__init__
中的错误检查移到顶部。
不要给self.abacus
默认设置,它只隐藏bug。
大多数方法都是从do_
开始的。就我个人而言,我会把它移除。把人物花在更好的名字上(如。count
是做什么的?)
目前,这意味着:
class AbacusColumn(object):
def __init__(self, current_line, signs):
self.current_line = current_line
self.signs = signs
class Abacus(object):
"""
Recombining tool.
Imagine each char in chars as digits, or beads, and count them accordingly to a base reference.
Imagine the base as the abacus lines.
'Counter' returns an integer equals to len(chars) raised to the power x = base, giving the number
of all possible bead states of the "abacus".
'Enumarate' prints each or all states.
"""
def __init__(self, chars=['+', '-'], base=2):
if base <= 1:
raise ValueError('Use argument "base" higher then one.')
self.counter = 0
self.states = []
self.abacus = [
AbacusColumn(0, [char + str(x+1) for char in chars])
for x in range(base)
]
self.count(base - 1)
def save_state(self):
state = ''.join(column.signs[column.current_line] for column in self.abacus)
self.states.append(state)
def line_reset(self, from_column):
for column in self.abacus[from_column:]:
column.current_line = 0
def line_increment(self, column):
self.counter += 1
self.save_state()
self.abacus[column].current_line += 1
def count(self, column):
high_column = len(self.abacus) -1
low_column = 0
if column < low_column:
self.counter += 1
self.save_state()
else:
column_max_line = len(self.abacus[column].signs) -1
column_cur_line = self.abacus[column].current_line
if column_cur_line == column_max_line:
column -= 1
self.count(column)
if ( column < high_column ) and ( column_cur_line < column_max_line ):
self.line_increment(column)
self.line_reset( column + 1 )
self.count(high_column)
if ( column == high_column ) and ( column_cur_line < column_max_line ):
self.line_increment(column)
self.count(column)
def enumerate(self):
return list(self.states)
counter
只跟踪len(self.states)
。把它移开。
count
是递归的,但不需要递归。尝试将其设置为while
,并使其成为yield
结果,而不是保存它们:
class Abacus(object):
def __init__(self, chars=['+', '-'], base=2):
if base <= 1:
raise ValueError('Use argument "base" higher then one.')
self.base = base
self.abacus = [
AbacusColumn(0, [char + str(x+1) for char in chars])
for x in range(base)
]
def gen_state(self):
return ''.join(column.signs[column.current_line] for column in self.abacus)
def line_reset(self, from_column):
for column in self.abacus[from_column:]:
column.current_line = 0
def count(self, column):
high_column = len(self.abacus) -1
while column >= 0:
column_max_line = len(self.abacus[column].signs) - 1
column_cur_line = self.abacus[column].current_line
if column_cur_line == column_max_line:
column -= 1
else:
yield self.gen_state()
self.abacus[column].current_line += 1
if column < high_column:
self.line_reset( column + 1 )
column = high_column
yield self.gen_state()
def enumerate(self):
return list(self.count(self.base - 1))
这突出说明类没有做任何有用的事情;将其分解为方法。
class AbacusColumn(object):
def __init__(self, current_line, signs):
self.current_line = current_line
self.signs = signs
def gen_state(abacus):
return ''.join(column.signs[column.current_line] for column in abacus)
def line_reset(abacus, from_column):
for column in abacus[from_column:]:
column.current_line = 0
def abacus(chars=['+', '-'], base=2):
if base <= 1:
raise ValueError('Use argument "base" higher then one.')
column = high_column = base - 1
abacus = [
AbacusColumn(0, [char + str(x+1) for char in chars])
for x in range(base)
]
while column >= 0:
column_max_line = len(abacus[column].signs) - 1
column_cur_line = abacus[column].current_line
if column_cur_line == column_max_line:
column -= 1
else:
yield gen_state(abacus)
abacus[column].current_line += 1
if column < high_column:
line_reset(abacus, column + 1)
column = high_column
yield gen_state(abacus)
print(list(abacus(base=5)))
这比原来的要简单得多。我很想删除AbacusColumn
,将abacus
拆分成两个并行列表,并翻转搜索它们的顺序,从而允许进一步简化:
def abacus(chars=['+', '-'], base=2):
if base <= 1:
raise ValueError('Use argument "base" higher then one.')
digit = [0] * base
while True:
yield ''.join(chars[current_line] + str(i) for i, current_line in enumerate(digit, 1))
for column in range(base):
if digit[column] == len(chars) - 1:
digit[column] = 0
else:
digit[column] += 1
break
else:
return
print(list(abacus(base=5)))
https://codereview.stackexchange.com/questions/75524
复制相似问题