上手小操作
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了上手小操作相关的知识,希望对你有一定的参考价值。
在编写自己的脚本时,python对于新开发人员来说是非常有用的,但是您也容易养成一些奇怪的习惯,或者编写一些不容易理解的脚本。
对于你自己的工作,这当然很好,但是如果你想和其他人合作,或者把你的工作包括在blender中,我们会鼓励你进行一些练习。
1 Style Conventions 风格惯例
对于Blender / Python开发,我们选择遵循Python建议的风格指南,以避免在我们自己的脚本中混合样式,并使在其他项目中使用Python脚本更容易。
如果你想对Blender做贡献的话,那么遵循我们的建议就会变得很容易。
我们遵循pep8 的编码风格,可以在这里找到here,下面是一个简洁的pep8标准列表:
- camel caps(类名使用驼峰大小写): MyClass
- 使用小写下划线分隔模块名: my_module
- 使用四个空格缩进 (不要使用tabs)
- 运算符前后加空格.
1 + 1
, not1+1
- 只使用明确的模块导入, (no importing
*
) - 不要在一行里编代码:
if val: body
, separate onto 2 lines instead.
除了pep8,我们还有其他用于blender python脚本的约定:
-
枚举使用单引号,字符串使用双引号
两者都是字符串,但在我们的内部API中,枚举是来自有限集合的惟一项。 eg.
bpy.context.scene.render.image_settings.file_format = ‘PNG‘ bpy.context.scene.render.filepath = "//render_out"
-
pep8要求每行不超过 79 个字符, 我们感觉这个太严格了,所以这一条可选。
我们周期性地在blender脚本上进行pep8遵从性检查,在此检查中添加的脚本添加这一行作为脚本顶部的注释。
# <pep8 compliant>
开启行字符长度检查
# <pep8-80 compliant>
2 User Interface Layout 用户界面布局
在编写UI布局时要记住一些要点:
-
UI 代码非常简单. 布局声明可以很容易地创建一个合适的布局。
大体规则是: 不要让布局声明的代码多于你实际要操作属性的代码.
布局例子:
-
layout()
基本的布局是从上到下Top -> Bottom.
layout.prop() layout.prop()
-
layout.row()
你想在一行中排列多个属性,使用row().
row = layout.row() row.prop() row.prop()
-
layout.column()
Use column(), 将属性按列排.
col = layout.column() col.prop() col.prop()
-
layout.split()
这可以创建一些复杂的布局. 例如,你可以将当前布局分割成两个紧挨着的列. 如果你只想排列两个属性时,不要使用split() 而是用 row()
split = layout.split() col = split.column() col.prop() col.prop() col = split.column() col.prop() col.prop()
声明的名字:
- row for a row() layout
- col for a column() layout
- split for a split() layout
- flow for a column_flow() layout
- sub for a sub layout (a column inside a column for example)
3 Script Efficiency 高效的脚本
3.1 List Manipulation (General Python Tips) 列表操作
3.1.1 Searching for list items 查找
在Python中,有一些方便的列表函数可以帮助您在列表中搜索。
即使你没有遍历列表,但这其实是Python在帮你做。 因此你要意识到这会降低你的脚本效率。
my_list.count(list_item) my_list.index(list_item) my_list.remove(list_item) if list_item in my_list: ...
3.1.2 Modifying Lists 修改
在Python中我们可以对列表进行添加和删除操作,但当列表长度改变时,这些操作是非常慢的。尤其是列表开始的元素,这样后续的每个元素索引都要改变。
向列表末尾添加 my_list.append(list_item)
or my_list.extend(some_list)
并且快速删除列表末尾的方法是 my_list.pop()
or del my_list[-1]
.
使用索引你可以my_list.insert(index, list_item)
or list.pop(index)
但这样会很慢
有时重建列表会很快,但也消耗内存
比方说你想删除列表中的所有三角形面。
不要像下面这样:
faces = mesh.tessfaces[:] # make a list copy of the meshes faces f_idx = len(faces) # Loop backwards while f_idx: # while the value is not 0 f_idx -= 1 if len(faces[f_idx].vertices) == 3: faces.pop(f_idx) # remove the triangle
相比之下使用列表推导新建一个列表会更快:
faces = [f for f in mesh.tessfaces if len(f.vertices) != 3]
3.1.3 Adding List Items 添加
如果想合并两个列表,不要用下面这个
for l in some_list: my_list.append(l)
而是要这样操作:
my_list.extend([a, b, c...])
插入有时也是需要的, 但是与在长列表后面添加相比还是很慢
下面这个例子展示了一个次佳的列子来将列表倒置.
reverse_list = [] for list_item in some_list: reverse_list.insert(0, list_item)
Python使用切片操作来提供更简便的操作,但你可能需要花点时间掌握它,一旦你掌握它 你就会非常依赖它:
some_reversed_list = some_list[::-1]
3.1.4 Removing List Items 删除
使用 my_list.pop(index)
,而不是 my_list.remove(list_item)
这要求你有元素的索引,但是更快。因为remove()
会搜索整个列表
下面这个例子说明了 remove将在一个循环中进行操作, 然后pop一个元素,这就是上面解释了为什么pop删除更快。
list_index = len(my_list) while list_index: list_index -= 1 if my_list[list_index].some_test_attribute == 1: my_list.pop(list_index)
下面这个例子展示了更快的删除方式, 可以在不破坏脚本功能的情况下改变列表顺序.这种方法先将你要删除的元素交换至最后.
pop_index = 5 # swap so the pop_index is last. my_list[-1], my_list[pop_index] = my_list[pop_index], my_list[-1] # remove last item (pop_index) my_list.pop()
当在一个长列表中删除时,这会有很好的速度。
3.1.5 Avoid Copying Lists 避免复制
当向一个函数传递 list/dictionary, 直接对列表进行操作要比返回一个新的列表快的多,因为这样Python不需要在内存中复制一份参数.
修改列表的函数比创建新列表的函数更有效
下面这个很慢,只有在不修改列表时才使用。
>>> my_list = some_list_func(my_list)
而下面这个就很快,因为没有重新分配内存并且没有复制操作
>>> some_list_func(vec)
还要注意,通过切片列表会复制python内存中的列表。
>>> foobar(my_list[:])
如果 my_list 包含10000个元素, 复制它将会消耗很多额外的内存.
3.2 Writing Strings to a File (Python General) 将字符串写入文件
这里有三种方法可以将多个字符串连接到一个字符串中。 这也适用于代码中涉及大量字符串连接的任何领域。
String addition
- 这是最慢的, 不要使用它 尤其是在循环中写入数据时.
>>> file.write(str1 + " " + str2 + " " + str3 + "\n")
String formatting
-当你要把浮点和整形数写入字符串时,用这个方法。
>>> file.write("%s %s %s\n" % (str1, str2, str3))
String join() function用于加入字符串列表
(可以是一个临时列表). 下面这个例子在字符串间添加了” ”,也可以添加 “” or ”, ”.
>>> file.write(" ".join([str1, str2, str3, "\n"]))
Join 在多个字符串间操作很快, string formatting 也很快 (尤其在转换数据类型时). String arithmetic 最慢.
3.3 Parsing Strings (Import/Exporting) 解析字符串
由于许多文件格式都是ASCII格式的,所以解析/导出字符串的方式会对脚本运行的速度产生很大的影响。
在将字符串导入Blender时,有一些方法可以解析字符串。
3.3.1 Parsing Numbers 解析数据
使用float(string)而不是eval(string),如果知道值将是int(string),float()也会为int工作,但是使用int()读取ints更快。
3.3.2 Checking String Start/End
如果你要检查某个字符串是不是以某个关键字开头,不要这样操作...
>>> if line[0:5] == "vert ": ...
而是...
>>> if line.startswith("vert "):
使用startswith()
会稍微更快 (approx 5%) and也避免了切片的长度与字符串长度不匹配的问题.
my_string.endswith(“foo_bar”) 也可以用来检查字符串的结尾.
如果不确定字母大小写, use the lower()
or upper()
string function.
>>> if line.lower().startswith("vert ")
3.4 Use try/except Sparingly 少量使用异常检查
try语句有助于节省编写错误检查代码的时间。
但是,如果每次都必须设置一个异常,那么try要明显慢一些,所以要避免在代码中执行多次循环并运行的区域使用try。
有些情况下,使用try比检查条件是否引起错误要快,所以这是值得尝试的。
3.5 Value Comparison 值比较
Python有两种方法来比较值a == b和a is b,不同的是= =可以运行对象比较函数__cmp__(),而is比较标识,这两个变量在内存中引用相同的项。
如果您知道您正在检查从多个地方引用的相同值,则 is 更快。
3.6 Time Your Code 检测你代码的性能
在开发脚本时,最好能让它意识到性能上的任何变化,这可以简单地完成。
import time time_start = time.time() # do something... print("My Script Finished: %.4f sec" % (time.time() - time_start))
以上是关于上手小操作的主要内容,如果未能解决你的问题,请参考以下文章