Introduction to Python/zh-cn: Difference between revisions

From FreeCAD Documentation
(Created page with "==模块==")
No edit summary
Line 287: Line 287:
print sum(45,34)
print sum(45,34)
}}
}}
借此来打印所需内容。关于更多python函数的信息,可参见于[http://www.diveintopython.net/getting_to_know_python/declaring_functions.html 此]。
借此来打印所需内容。关于python函数的更多信息,可参见于[http://www.diveintopython.net/getting_to_know_python/declaring_functions.html 此]。


==模块==
==模块==

Revision as of 09:59, 27 June 2019

Macros
Python scripting tutorial
(2018年 11月) FreeCAD原本的设计是配合Python 2协同工作。许多功能可通过Python 3来实现,有些却不能。令FreeCAD完全支持Python 3是一项正处于开发中的任务。本文描述的内容均使用的是Python 2,但大多代码也可同样用于Python 3。特别是print()函数要优于老式的print语句。

本简短教程可供Python新手参阅。Python是一种开源、支持多平台的 编程语言。Python有几种不同于其他常见语言的特性,使之与众不同,而且还易于对此毫无经验的人使用:

  • Python被设计为一种易于常人阅读的语言,因此方便学习且代码容易理解。
  • Python是一种解释型语言,也就是它并非C那样的编译型语言,在程序运行前无需进行编译。您所编写的代码将如您所愿即刻逐行执行。由于代码可放慢脚步逐行执行,因此更便于学习与查找代码中错误。
  • 可将Python嵌入其他程序用作脚本语言。FreeCAD中配有一个内嵌的Python解释器;您可以在FreeCAD中编写Python代码来控制FreeCAD的各种组件,比如用来创建一个几何图形等等。这是一种极其强大的工具,程序员可通过它编写代码,以此取代仅能点击标有"创建球体"按钮的机械式操作;您可以借此自由方便地构建自己的工具,创建精准的几何图形,但程序员可能无法预览待创建的图形。
  • Python具有可扩展性,您可以轻松地为它接入新的模块来拓展其功能。例如,您可以借助特定模块使Python可以读写jpg图片、与twitter进行交互,甚至调度任务在操作系统上的执行,等等。

我们墙裂鼓励您亲自将代码输入至Python解释器。经过我们的激烈讨论,认为重点在于令代码跑起来,并显示出结果。不运行代码根本不会理解Python。因此,快动手吧!以下是相关简介,而并非完整的教程。但是我们希望这能为您深入理解FreeCAD中的内部机制而提供相关的基础知识。

解释器

用户在写程序时通常会打开文本编辑器或特定的编程环境(一般是配有其他辅助工具的文本编辑器),借此编写代码、编译并执行。当代码中存在一个或多个错误时,程序往往就跑不起来了。这时,您可以根据报错信息定位错误的根源。再返回编辑器,纠正错误,再次运行,如此往复直到程序可按预期执行为止。

在整个处理流程中,Python解释器的内部工作原理对用户而言都是透明的。所谓的解释器是一种附有命令提示符的Python窗口,在此,您可以方便地输入Python代码。如果您在自己的计算机上安装了Python(如果系统为Windows或Mac,从Python website下载;如果系统是GNU/Linux,则借助软件包资料库(package repository)进行安装), 您可从开始菜单中开启Python解释器。但是,FreeCAD中的下方窗口中也集成了Python解释器:

(如果没有找到,请依次点选View --> Panels --> Python console。)

解释器中显示着Python的版本,a >>>即为命令提示符,您可以在此输入Python代码。在解释器中编写代码很简单:一行对应一个指令。在按下回车键时,将执行本行代码(此前将自动在背后进行代码编译)。例如,请尝试编写下列代码:

print "hello"

print是一种特殊的Python关键字,顾名思义,它负责在屏幕上打印内容。当您按下回车键时,将处理所输入的代码,最后打印"hello"这条消息。如果您得到了一个错误信息,例如,让我们来这样写:

print hello

Python将告知我们,它不知道hello是神马。利用"符号可方便地指定字符串内的多个字符,用编程术语来讲,即一段文本。若不使用"符号,则print命令将认为hello并非一段文本,而是一个特殊的Python关键字。而重点在于,您会立即得到一个因此而产生的错误消息。通过按“up”键(或者在FreeCAD解释器中按CTRL+up),您将返回上一条编写的命令,并对此进行修正。

Python解释器中还设有一个内建的帮助系统。尝试输入:

help

或者,例如,当我们不知道上述打印hello命令哪里出了问题,而希望得知与"print"命令有关的信息时:

help("print")

您将得到一条既长又完整的print命令功能的描述。

现在让我们来全面了解一下Python解释器,先从最关键的部分入手吧!

变量

实话实说,打印"hello"字符串没什么意思。更有趣的是打印我们没有提前得知的内容,或令Python来为我们求出所需的内容。这便是引入变量这一概念的原因。简而言之,变量即为:存有特定值的名称。例如,输入下列内容:

a = "hello"
print a

我猜您已经知道这段代码的背后究竟发生了什么,我们将"hello"存在了"a"名下。现在,"a"在Python环境中已经不再是一个未知的名称了!我们可以在各处运用它,例如,将其用于上述打印命令中。我们几乎可以使用能想到的任意名称,仅需遵循一些简单的规则即可,比如名称中不能夹带空格与特定的标点符号。例如,我们可以这样写:

hello = "my own version of hello"
print hello

看到了吗?现在的hello是一个有定义的单词。但是,当我们无意中选取了Python中已经存在的名称作为变量名时会发生什么呢?这里,我们选取"print"作为变量名来保存字符串:

print = "hello"

Python很机智,告诉我们:没戏。它自己有一些"保留"的关键字是用户不能修改的。但是我们自己的变量却可以任意修改,这就是它们为什么被称为变量:内容可变。例如:

myVariable = "hello"
print myVariable
myVariable = "good bye"
print myVariable

我们可以改变myVariable的值。也可以复制变量:

var1 = "hello"
var2 = var1
print var2

请注意,关键的是要给变量起个有意义的名称。这是因为,经过一段时间后您可以会忘记变量"a"表示的是什么。例如,将某个变量命名myWelcomeMessage后,您可以轻松回忆起设立此变量的目的。这样便可以使您的代码直观地表达出自身的意义。

字母的大小写也非常重要。myVariable与myvariable是两个截然不同的变量,区别就在于其中字母v的大小写上。如果您输入print myvariable,系统就会返回一个“未定义”错误。

数字

当然,您一定知道编程的过程中要处理各种数据,不仅仅是前面提到的字符串,还有数字等等。有一点十分重要,Python必须知道它要处理的数据类型。在我们此前示范的打印hello的示例中,print命令知道我们提供的"hello"是个字符串。这是因为我们利用了引号"通知了print命令,它接下来要处理的是个文本字符串。

我们可以通过Python中的关键字type来检测一个变量的数据类型:

myVar = "hello"
type(myVar)

在这里,它将告诉我们,变量myVar中的内容是一个'str',也就是Python术语中字符串的简写。我们也可以使用Python提供的其他类型数据,如整数与浮点数:

firstNumber = 10
secondNumber = 20
print firstNumber + secondNumber
type(firstNumber)

这就更有趣了,不是吗?这就是说,我们现在拥有了一个强大的计算器了!来看看她是怎么跑起来的。Python知道10与20窦唯整数。所以他们在内部被存为"int",这样Python就可以把它们看作是整数并加以处理。再来看看下面的计算结果:

firstNumber = "10"
secondNumber = "20"
print firstNumber + secondNumber

看到了吗?在此,我们强制Python把这两个变量看作是文本而非数字。Python可以把两条文本合二为一,但是却不会计算两者之和。只不过我们现在要讨论的其实是整数。Python还支持浮点数。整数与浮点数的区别在于前者没有小数部分,而浮点数却有:

var1 = 13
var2 = 15.65
print "var1 is of type ", type(var1)
print "var2 is of type ", type(var2)

可以肆无忌惮地进行整数与浮点数的混合运算:

total = var1 + var2
print total
print type(total)

当然,最后的计算结果依旧存在小数部分,细不细?在此,Python会自动将结果记作一个浮点数。包括上述情况在内的若干情景中,Python会自动推测到底使用哪种类型。但在另外一些场景中却不是这样,例如:

varA = "hello 123"
varB = 456
print varA + varB

执行上述命令后,我们将得到一个错误信息。varA是一个字符串,而varB则是一个整数,Python不知道该如何处理了。但是,我们可以强制Python将数据的类型互相转化:

varA = "hello"
varB = 123
print varA + str(varB)

两个变量现在都是字符串,可以顺利执行后续操作了!请注意,变量varB的“字符串化”仅发生在打印过程中,但是其自身并没有发生任何改变。如果希望将varB永久转换为字符串,我们需要这样做:

varB = str(varB)

我们也可以通过int()与float()将数据转换为整数与浮点数类型:

varA = "123"
print int(varA)
print float(varA)

关于Python命令的备注

您一定注意到了,我们在本部分中用打印命令显示了几种不同的数据。我们以逗号作为分隔符打印了变量、所求之和以及其他内容,甚至还打印了像type()这样的命令所返回的结果。您可能会发现,若执行下列两种命令:

type(varA)
print type(varA)

将得到同样的结果。这是因为我们的执行环境处于解释器中,每个结果都会被自动打印出来。而当我们编写了更为复杂的程序,却在解释器外执行时,便不会自动打印任何内容了,这就要靠自己加入print命令。自此,为了让进度加快,我们就不再使用print命令了。这样,就可以将代码简单地写作:

myVar = "hello friends"
myVar

您一定有看到,大多Python命令(或关键字)如type()、int()、str()等等都是用括号来包围命令内容的。唯一的例外则是print命令,这其实也不算是个“例外”,因为它也可以按这种形式来执行: print("hello")。但是,由于其使用频率过高,因此Python设计者们决定针对它为用户提供一种简化写法。

列表

另一种有趣的数据类型为列表(list),它是一种简单的其他类型的数据集合。与定义字符串使用" "相仿,我们用[ ]来定义列表:

myList = [1,2,3]
type(myList)
myOtherList = ["Bart", "Frank", "Bob"]
myMixedList = ["hello", 345, 34.567]

不难看出,列表可容纳其他类型的数据。列表的用处体现在可以把多个变量“打包”在一起。您可以对列表里面的东东执行任意类型的操作,比如统计其中元素的数量:

len(myOtherList)

或者取出列表中的任意一项:

myName = myOtherList[0]
myFriendsName = myOtherList[1]

可以看到,len()命令返回的是列表中元素的总数,且它们在列表中的“位置”是以0开始计算的。列表中第一项的位置总为0,因此,在myOtherList列表中,"Bob"位居第2。我们可以对列表进行更多的操作,例如对元素进行排序、增减其中的元素,这可以参考这里(已废?)

其实这里有个有趣的事情:文本字符串与字符构成的列表极其相似!试着这样做一下:

myvar = "hello"
len(myvar)
myvar[2]

一般而言,您对字符串所执行的操作也同样可以应用于列表上。事实上,列表与字符串都是顺序结构。

除了字符串、整数、浮点数与列表之外,Python中还有更多其他的内建数据类型,例如dictionaries,或者,您甚至可以通过创建自己的数据类型。

缩进

列表的一种极其酷炫用法是遍历每一项,并对其依次进行处理。例如,来看下面这个示例:

alldaltons = ["Joe", "William", "Jack", "Averell"]
for dalton in alldaltons:
   print dalton + " Dalton"

我们利用"for ... in ..."命令迭代(iterate,编程术语)列表中的每一项,并针对每项执行特定操作。注意到这特殊的语法:for命令以:作为结尾,表示下面为单个或多个命令所构成的执行块。 在解释器中,在输入以:结尾的命令行后,命令提示符将变为...这意味着Python知道(:)行后必有更多的输入内容。

那末,Python是如何知道for...in语句中还有多少行命令要执行呢?这是因为Python采用了缩进策略。即,在for...in语句后,您是不会直接输入下一行内容的。而是要以一个(多个)空格符,或一个(多个)tab符作为开头。其他编程语言则采用其他方式,如将所有内容都置于括号内等等。 只要您按上述同样的缩进编写后续代码,Python就会认为它们都是for-in块的组成部分。如果代码块中第一行以2个空格作为缩进,而下一行以4个空格作为缩进,便会产生一个错误。 如果代码块中的内容编写结束,只需令下一行无缩进,或简单地按下回车键退出for-in块即可。

缩进策略这么吊炸天,究其原因是为了程序的可读性。如果用了较大的索引(比如用tab取代空格,因为tab比空格占用空间更大), 在编写规模很大的程序时,其逻辑看起来就会清晰不少。我们随后会看到,for-in以外的不少命令也可以附有缩进代码块。

for-in命令可用于将某些操作执行多次的情况。例如,它可以搭配range()命令使用:

serie = range(1,11)
total = 0
print "sum"
for number in serie:
   print number
   total = total + number
print "----"
print total

(如果您通过复制、粘贴的方式在解释器中运行上述代码示例,会发现其中的文本块抛出了一个错误。在这种情况下,我们可以从头一直复制到缩进块的结尾,即total = total + number行的结尾,并将其粘贴至解释器中。这时在解释器中(连续)输入<enter>,直到解释器不再显示三个点的符号,并执行上述代码。接着再将最后两行代码复制到解释器中以上换行符<enter>后。现在,解释器应该能显示出程序的输出结果。)

如果您在解释器宏输入help(range),将会看到:

range(...)
    range(stop) -> list of integers
    range(start, stop[, step]) -> list of integers

方括号表示可选参数。不难看出,range()的所有参数都为整数。在以下示例中,我们将通过int()把所有range()的参数都强制转换为整数。

decimales = 1000                     # for 3 decimales 
#decimales = 10000                   # for 4 decimales ...
for i in range(int(0 * decimales),int(180 * decimales),int(0.5 * decimales)):
    print float(i) / decimales

或用range()实现更为复杂的逻辑:

alldaltons = ["Joe", "William", "Jack", "Averell"]
for n in range(4):
   print alldaltons[n], " is Dalton number ", n

可以发现,range()命令也有一些奇怪的特殊性,即它以0开始计数(在没有指定起始编号的情况下),且最后的编号要比您所指定的值小1。当然,这样也可以令它与其他Python命令配合得很顺利。例如:

alldaltons = ["Joe", "William", "Jack", "Averell"]
total = len(alldaltons)
for n in range(total):
   print alldaltons[n]

缩进搭配if命令也是另一种有趣的用法。如果满足了特定条件,则执行对应的if代码块,例如:

alldaltons = ["Joe", "William", "Jack", "Averell"]
if "Joe" in alldaltons:
   print "We found that Dalton!!!"

当然啦,根据上述条件则一定会打印第一条语句,但是现在来以下列代码加以取代:

if "Lucky" in alldaltons:

这样就不会打印任何内容了。我们也可以使用一个else:语句(字符串有问题?(Dalton->Lucky?)):

alldaltons = ["Joe", "William", "Jack", "Averell"]
if "Lucky" in alldaltons:
   print "We found that Dalton!!!"
else:
   print "Such Dalton doesn't exist!"

函数

这里列出了 Python的标准命令。在Python的最新版本中,大约存在30条命令,我们刚刚尝试了其中的一部分。但是,您是否曾想过“发明”自己的命令?答案是:是的,我们可以,而且还相当简单。事实上,向Python添加其他的模块做的就是这件事:这些模块向Python增添了可供您使用的命令。自定义命令在Python中被称为函数,实现方法如下:

def printsqm(myValue):
   print str(myValue)+" square meters"
 
printsqm(45)

(如果直接复制、粘贴则又会出现一次错误,处理方式与上次类似:第一次仅需复制到缩进部分的结尾,也就是将开头至" square meters"复制到解释器中,(连续)按回车符<enter>,直到不再显示三点符号,这时再复制、粘贴剩余部分,也就是此程序中的最后一行。)

看吧,很简单:利用def()命令即可定义一个新的函数。您可以为它起名,在括号里定义其参数,在函数中处理这些参数。参数即将传入函数中的数据。例如,现在来考察len()命令。如果仅写len(),则Python将提示您需要为之提供一个参数。即,您需要用len()来做些什么,对吧?比如说,您可以用len(myList)来获取myList的长度。此时,myList便是您传入len()函数的参数。len()函数的定义便是如此,使之知道如何处理传入的参数。而我们刚刚做的也正是如此。

"myValue"可以为任意有效名称,仅用于定义它的目标函数之中。它只是您为参数起的名字,同时还负责告知函数所需的参数数量。例如,您可以试着这样写:

printsqm(45,34)

这将导致一个错误。原因是此函数实际仅接收一个参数,但是却为它输入了两个参数:45与34。我们可以编写下列函数:

def sum(val1,val2):
   total = val1 + val2
   return total

sum(45,34)
myTotal = sum(45,34)

在这段代码中,我们令函数接收两个参数,求其和,并返回其值。函数返回的数据很有用,因为我们可以继续对其结果进行处理,例如,这里将函数返回的结果存于myTotal变量之中。当然,由于我们身处解释器之中,所有内容都可打印出来,于是就可以这样做:

sum(45,34)

这将在屏幕中打印求和结果,但是若没有处于解释器中,由于函数里没有任何打印语句,所以在屏幕中不会看到任何内容。我们就需要这么做:

print sum(45,34)

借此来打印所需内容。关于python函数的更多信息,可参见于

模块

Now that we have a good idea of how Python works, we'll need one last thing: How to work with files and modules.

Until now, we wrote Python instructions line by line in the interpreter, right? What if we could write several lines together, and have them executed all at once? It would certainly be handier for doing more complex things. And we could save our work too. Well, that too, is extremely easy. Simply open a text editor (such as the windows notepad, Linux gedit, emacs, or vi), and write all your Python lines, the same way as you write them in the interpreter, with indentations, etc. Then, save that file somewhere, preferably with a .py extension. That's it, you have a complete Python program. Of course, there are much better editors than notepad, but it is just to show you that a Python program is nothing else than a text file.

To make Python execute that program, there are hundreds of ways. In windows, simply right-click your file, open it with Python, and execute it. But you can also execute it from the Python interpreter itself. For this, the interpreter must know where your .py program is. In FreeCAD, the easiest way is to place your program in a place that FreeCAD's Python interpreter knows by default, such as FreeCAD's bin folder, or any of the Mod folders. (In Linux, you probably have a directory /home/<username>/.FreeCAD/Mod, let's add a subdirectory to that called scripts where we will put the text file.) Suppose we write a file like this:

def sum(a,b):
    return a + b

print "myTest.py succesfully loaded"

and we save it as myTest.py in our FreeCAD/bin directory (or on Linux to /home/<username>/.FreeCAD/Mod/scripts.) Now, let's start FreeCAD, and in the interpreter window, write:

import myTest

without the .py extension. This will simply execute the contents of the file, line by line, just as if we had written it in the interpreter. The sum function will be created, and the message will be printed. There is one big difference: the import command is made not only to execute programs written in files, like ours, but also to load the functions inside, so they become available in the interpreter. Files containing functions, like ours, are called modules.

Normally when we write a sum() function in the interpreter, we execute it simply like that:

sum(14,45)

Like we did earlier. When we import a module containing our sum() function, the syntax is a bit different. We do:

myTest.sum(14,45)

That is, the module is imported as a "container", and all its functions are inside. This is extremely useful, because we can import a lot of modules, and keep everything well organized. So, basically, everywhere you see something.somethingElse, with a dot in between, that means somethingElse is inside something.

We can also import our sum() function directly into the main interpreter space, like this:

from myTest import *
sum(12,54)

Basically all modules behave like that. You import a module, then you can use its functions: module.function(argument). Almost all modules do that: they define functions, new data types and classes that you can use in the interpreter or in your own Python modules, because nothing prevents you from importing other modules inside your module!

One last extremely useful thing. How do we know what modules we have, what functions are inside and how to use them (that is, what kind of arguments they need)? We saw already that Python has a help() function. Doing:

help()
modules

Will give us a list of all available modules. We can now type q to get out of the interactive help, and import any of them. We can even browse their content with the dir() command

import math
dir(math)

We'll see all the functions contained in the math module, as well as strange stuff named __doc__, __file__, __name__. The __doc__ is extremely useful, it is a documentation text. Every function of (well-made) modules has a __doc__ that explains how to use it. For example, we see that there is a sin function in side the math module. Want to know how to use it?

print math.sin.__doc__

(It may not be evident, but on either side of doc are two underscore characters.)

And finally one last little goodie: When we work on a new or existing module, it's best to replace the file extension with py such as: myModule.FCMacro => myModule.py. We often want to test it so we will load it as above.

import myModule
myModule.myTestFunction()

But what if we see that myTestFunction() doesn't work correctly? We go back to our editor and make changes. Then, instead of closing and reopening the python interpreter, we can simply update the module like this:

reload(myModule)

This file renaming is because Python doesn't know about the extension FCMacro.

However, there are two alternates: Inside the one macro use Python's exec or execfile functions.

f = open("myModule","r")
d = f.read()
exec d

or

execfile "myModule"

For Python 3.xxx replace this code with:

exec(open("C:/PathToMyMacro/myMacro.FCMacro").read())

To share code across macros, you can access the FreeCAD or FreeCADGui module (or any other Python module) and set any attribute to it. This should survive the execution of the macro.

import FreeCAD
if hasattr(FreeCAD,"macro2_executed"):
    ...
else:
    FreeCAD.macro2_executed = True # you can assign any value because we only check for the existence of the attribute
    ... execute macro2

Starting with FreeCAD

Well, I think you now have a good idea of how Python works, and you can start exploring what FreeCAD has to offer. FreeCAD's Python functions are all well organized in different modules. Some of them are already loaded (imported) when you start FreeCAD. So, just do

dir()

and read on to FreeCAD Scripting Basics.

Of course, we saw here only a very small part of the Python world. There are many important concepts that we didn't mention. There are three very important Python reference documents on the net:

Be sure to bookmark them!