提问人:jonathan gabizon 提问时间:11/11/2023 最后编辑:mkrieger1jonathan gabizon 更新时间:11/12/2023 访问量:57
在遍历 RGB 颜色时,如何减少代码重复?
How can I reduce code duplication when iterating through RGB colors?
问:
我编写了一个脚本,以预设的增量遍历 3 个 RGB 值。
您会注意到六个循环的复制/粘贴,每个循环都有变化。
我想这六个部分可以组合成一个或两个父循环,通过一个子循环而不是 6 个递增。这有意义吗?
我的目标是减小此代码的大小。
代码如下:
color_numbers = [0,255]
increment_between_values = [64,64,63,64]
r_val=[]
g_val=[]
b_val=[]
rgb_values=[]#FORMAT: 255,255,255
def color_function():
color_base_b = color_numbers[0]
color_top_g = color_numbers[1]
color_base_r = color_numbers[0]
for x in range(len(increment_between_values)):
r_val.append(color_numbers[0])
g_val.append(color_numbers[1])
color_base_b += increment_between_values[x]
b_val.append(color_base_b)
for x in range(len(increment_between_values)):
r_val.append(color_numbers[0])
color_top_g -= increment_between_values[x]
g_val.append(color_top_g)
b_val.append(color_base_b)
for x in range(len(increment_between_values)):
color_base_r += increment_between_values[x]
r_val.append(color_base_r)
g_val.append(color_top_g)
b_val.append(color_base_b)
for x in range(len(increment_between_values)):
r_val.append(color_base_r)
g_val.append(color_top_g)
color_base_b -= increment_between_values[x]
b_val.append(color_base_b)
for x in range(len(increment_between_values)):
r_val.append(color_base_r)
color_top_g += increment_between_values[x]
g_val.append(color_top_g)
b_val.append(color_base_b)
for x in range(len(increment_between_values)):
color_base_r -= increment_between_values[x]
r_val.append(color_base_r)
g_val.append(color_top_g)
b_val.append(color_base_b)
for t in range(len(r_val)):
print(str(r_val[t])+","+str(g_val[t])+","+str(b_val[t]))
color_function()
据我所知,每个循环的变化如下: 循环 1:初始值 循环 2:初始值 循环 3:向上递增 循环 4:最高值 循环 5:最高值 循环 6:递增
这六个步骤分别针对 R、G 和 B 进行偏移 例如:当 R 在循环 1 上时,G 在循环 3 上,B 在循环 5 上。 还有第二个循环,从 + 切换到 -(递增,递减)
我希望我已经清楚地解释了这一点 代码按原样工作,我只是想了解如何优化它。
答:
你可以利用这样一个事实,即只有一个 0,一个 255,另一个东西,如果它从 0 开始,则向上迭代,如果它从 255 开始,则向下迭代
def mycf():
c=[0,0,0] # Starting point
l=[0,64,128,192,255] # Possible values
res=[] # Result
for p0 in range(3): # Where is 0 in c
c[p0]=0
for p255 in range(3): # Where in 255 in c
if p255==p0: continue # Cannot be where is 0
c[p255]=255
pother=3-p0-p255 # Where is the other value (sum p0+p255+pother = 0+1+2 (in another order) = 3. So 3-p0-p255 is the pother)
if c[pother]!=l[0]: l=l[::-1] # Iterates possible value from where we are. That is either 0 or 255. So if we are not at l[0], reverse it
# The if is for clarity. In fact we could have started with l=[255,192,128,64,0] and reverted each times inconditionnaly
for x in l[1:]:
c[pother]=x
res.append(list(c))
return res
Itertools瑜伽
如果你真的想减少代码行数,我们可以用一些迭代工具做一个单行(但一大行)
import itertools
def listRgb(*p):
return list(itertools.chain(*itertools.chain(*zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2)))))
listRgb(0,64,128,192,255)
解释:
itertools.permutations([0,255,x])
是 0,255 和 x 的所有组合,以确定性顺序 (0,255,x)、(0,x,255)、(255,0,x)、...或者更准确地说,它是一个迭代器,遍历这些三元组(其中 6 个)
迭代器也是如此,因为 x 是 4 个值 64,128,192,255 中的任何一个。因此,如果我们展开这个“迭代器的迭代器”,我们将得到 4 个序列,每序列 6 个三元组序列。(itertools.permutations([0,255,x]) for x in p[1:])
因此,返回由每个序列的一个元素组成的四胞胎。所以三胞胎四胞胎的迭代器。其中 6 个。对应于 0、255 和 x 的每个可能仿真,以及三元组的每个 x 值。按 x 的升序排列。我们快到了。除了,我们希望它们在一半的时间里按降序排列。zip(*(itertools.permutations([0,255,x]) for x in p[1:]))
zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1]))
降序是同一回事吗
请注意,第一个跳过 0(这与上一次迭代的最后一个是多余的),而第二个跳过 255(同样)。
因此,我们对第一个序列(升序序列)中的一半感兴趣,对第二个序列中的一半感兴趣。
我们可以用 2 个切片之一islice
itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2)
保持升序序列
和降序序列itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2)
我们可以用 zip 从这些半部分中挑选一个(这是一种从我们挑选序列的地方交替的方式)
zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2))
快到了。
由于我们在这里拥有的是 4 个三胞胎的成对(从最后一个 zip 开始);其中 3 个,即 4 个三胞胎的三元组(或者,准确地说,迭代器,如果我们迭代它,它会计算它。目前,我们还没有进行任何计算。它只是一个迭代器),我们需要连接结果。由于 itertools 的全部意义在于将事物保留为迭代器(在我们真正迭代事物之前不使用任何内存或 cpu),而不是将其转换为列表并将它们连接起来,因此我使用它将一个接一个地迭代 3 对中的每一对,以获得一个 6 个 uplet,chain
迭代器也是如此,迭代 6 个 4 个三元组。同样,我们只想迭代三元组。所以,再说一遍itertools.chain(*zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2)))
chain
itertools.chain(*itertools.chain(*zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2))))
是最终的迭代器。值为三元组的那个
事实上,它应该是我的函数返回的内容(而不是返回它)。因为列表会创建整个列表。也许你只是想一个接一个地枚举这些颜色,而没有必要将它们保留在内存中。我在我的函数中添加只是为了演示。但是你的问题是,所以没有必要建立一个列表。list
list
iterate
例如
import itertools
def itRgb(*p):
return itertools.chain(*itertools.chain(*zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2))))
for r,g,b in itRgb(*range(256)):
print(r,g,b)
使用步骤 1 打印 1530 值,而无需生成这些值的列表
所以
tl;博士
您可以使用单行代码迭代颜色:
p=[0,64,128,192,255]
for r,g,b in itertools.chain(*itertools.chain(*zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2)))):
print(r,g,b)
评论
m=LinearSegmentedColormap.from_list('m', [(0,2,0), (0,0,2),(2,0,0),(0,2,0)], N=10000)
np.round(m(np.linspace(0.042,1,24))*255)[:,:3]
matplotlib
变量名称过长、具有误导性且使用不一致。而且按索引循环很糟糕。建议:
def color_function():
r = b = color_numbers[0]
g = color_numbers[1]
def append():
r_val.append(r)
g_val.append(g)
b_val.append(b)
for inc in increment_between_values:
b += inc
append()
# the other five loops similarly...
for rgb in zip(r_val, g_val, b_val):
print(*rgb, sep=",")
仍然是六个循环,但更短/更清晰,很容易看到发生了什么。
评论