五、上手小操作

Best Practice

  在编写自己的脚本时,python对于新开发人员来说是非常有用的,但是您也容易养成一些奇怪的习惯,或者编写一些不容易理解的脚本。

  对于你自己的工作,这当然很好,但是如果你想和其他人合作,或者把你的工作包括在blender中,我们会鼓励你进行一些练习。

1  Style Conventions  风格惯例

  对于Blender / Python开发,我们选择遵循Python建议的风格指南,以避免在我们自己的脚本中混合样式,并使在其他项目中使用Python脚本更容易。

  如果你想对Blender做贡献的话,那么遵循我们的建议就会变得很容易。

  我们遵循pep8 的编码风格,可以在这里找到here,下面是一个简洁的pep8标准列表:

  • camel caps(类名使用驼峰大小写): MyClass
  • 使用小写下划线分隔模块名: my_module
  • 使用四个空格缩进 (不要使用tabs)
  • 运算符前后加空格. 1, not 1+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 + "
")

  String formatting -当你要把浮点和整形数写入字符串时,用这个方法。

>>> file.write("%s %s %s
" % (str1, str2, str3))

  String join() function用于加入字符串列表 (可以是一个临时列表). 下面这个例子在字符串间添加了” ”,也可以添加 “” or ”, ”.

>>> file.write(" ".join([str1, str2, str3, "
"]))

  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))
原文地址:https://www.cnblogs.com/wildbloom/p/7756780.html