## 2.1 语言起源
和普通语言一样,编程语言也有分类。Python是高级语言,就是说这种语言被设计为易于理解。与之相反的是极低级别的语言(通常叫机器码),绝对不容易理解。两者中间的是C或C++那样的语言,在机器码之上提供了一个抽象层。如前所述,Python更进了一步,意味着它易于理解(接近英文语法),不需要去处理复杂功能,比如内存回收,甚至声明变量!
我们真幸运。
Python首发于1991年,从那以后它自由发展,成为一个用户成千上万的语言。Python文档声明”Python百搭“、"Python哪儿都有”、“Python平易近人”、“Python是开放的”。更多Python语言和开发的信息请参考:[http://www.python.org](http://www.python.org).
假设即使以前你没有过编程经验,我仍然觉得以下例子对你并不难:
```python
import rhinoscriptsyntax as rs
somenumber = rs.GetReal("Line length")
line = rs.AddLine([0,0,0], [somenumber,0,0])
print "Line curve inserted with id", line
```
当然你可能对[0,0,0]实际是什么并没有概念,对rs.GetReal()感动困惑,但是整体上这和你去杂货店使用的沟通语言差不多:
```
要求Rhino给一个叫'somenumber'的东西赋值.
告诉Rhino从世界原点到'somenumber'定义的点加一条线
打印一条成功信息
```
Python和英文的互译并不会很难,至少在上面这个例子体现的级别来说是很容易的。有办法让代码写得不那么容易理解,但这不是你应该骄傲的点。语法与英文相似是有好处的,建议还是遵守。
如前文述,语法有3个主要因素,上面的脚本都使用到了:
1. 流程控制 » 基于第2行的输出,有些行可能不会运行
2. 变量 » somenumber用于存储一个变量
3. 交互 » 用户被要求输入,并且有结果输出
## 2.2 流程控制
在Python中,可使用流程控制跳过某些行的执行,或执行某些行多次。也可以使用流程控制跳转到其他不同的行,然后再返回。在代码中,可以添加条件语句,达到屏蔽某些部分的作用。If…Else…结构是条件语句的例子,但是这部分稍后再讨论。
一个典型的条件语句如:
```
你必须达到这么高(1.5米)才能坐过山车。
```
这行‘代码’使用了一个条件(高于1.5米),来评估你最后到底能不能坐过山车。像这样的条件语句可以无限的串在一起。也可以增加其他条件,比如最大身高,或者最大体重,或者禁止带眼镜或禁止有心脏问题的人玩过山车。
除了跳过某些行,也可重复执行某些行。我们可以指定一个固定数字:
```
添加5汤匙糖。
```
或者同样使用条件判断:
```
持续添加牛奶直到面团松软。
```
在编程术语里重复执行某些行叫‘循环’。有多种循环类型,但是它们基本上功能都差不多。后面会细讲。
## 2.3 变量
无论何时我们都希望代码是动态的,因此必须确保它能够处理各种不同的情况。为了做到这一点,我们必须有存储变量的能力。例如,我们可能想在我们的三维模型中存储一组曲线,这样我们就可以在后面的阶段删除它们。或者,我们的程序需要添加一条从鼠标指针到三维原点的线。或者我们需要检查当前日期,检查软件是否已经过期。这些信息在编写脚本的时候是没有的。
每当我们需要存储数据/进行计算/逻辑运算时,我们都需要变量来记住结果。由于这些操作是动态的,我们不能在执行前将它们添加到脚本中。我们需要占位符。
在上一页的例子中,名为 *"somenumber "* 的东西是一个数字占位符。它一开始只是一个名称,没有附加任何数据,但在以下一条代码中它将被赋予一个数字值:
```python
somenumber = rs.GetNumber("Line length")
```
然后,当把直线添加到Rhino时,我们会使用到这个特定的值:
```python
curve = rs.AddLine([0,0,0], [somenumber,0,0])
```
所有其他定义线条对象的坐标都是硬编码在脚本中的。变量被读取或重新赋值的频率没有限制,但它永远不能包含一个以上的值,也没有撤销系统来获取过去的值。除了数字之外,我们还可以在变量中存储其他类型的数据。目前,我们只限于四个最基本的变量,再加上一个用于纠错的特殊变量:
1. 整型
2. 双精度浮点型
3. 布尔型
4. 字符串
5. None变量
### 2.3.1 整型和双精度浮点型
整型和双精度浮点型都是数字变量类型,意味着它们可以用来存储数字。它们分别用于存储不同类型的数字,这就是为什么我们最终会有一个以上的类型。整型只能用于存储整数。它们的范围大致从-20亿到+20亿。这个区间之内的每一个整数都可以用一个整型来表示。整数几乎只用于计数目的(与计算相反)。
双精度浮点型是数字变量,可以存储带小数的数字。双精度浮点型的上限可以达到1.8×10<SUP>308</SUP>,下限可以达到5.0×10<SUP>-324</SUP>, 尽管在实践中,可以准确表示的数字范围要小得多。那些不熟悉科学符号的人不必担心,我不会一直这样写数字。只要知道双精度浮点型的数字范围确实很大就够了。
<div align=center><img src="https://gitee.com/al666ex/developer-rhino3d-com/raw/master/images/primer-integers.svg" width="75%"></div>
<!--TODO: The font in the SVG above is not rendeirng correctly. What Font to use -->
所有可能的双数和整数的集合是不连续的;它有间隙。0和0之间不存在整数,零和5.0×10<SUP>-324</SUP>之间不存在双数。事实上,双数的间隙要小得多,只是因为我们选择了一个接近零的数字。当我们向更大数字移动时,两个相邻的双数之间的差距也会变大,当我们接近范围的极限时,差距大到足以容纳银河系。2×10<SUP>300</SUP>减去10亿仍然是2×10300,所以在使用极其大的数字时要小心。通常情况下,我们从不涉足32位计算开始崩溃的区域,我们倾向于将自己限制在我们实际能够应付的数字上。
用于处理数值变量的Python语法应该非常易于理解:
```python
x = 15 + 26 # x 等于 41
x = 15 + 26 * 2.33 # x 等于 75.58
x = math.sin(15 + 26) + math.sqrt(2.33) # x 等于 1.368
```
你可以使用`print()`方法来显示这些计算的结果。`print()`方法将在命令行中显示该值。:
```python
x = 2 * math.sin(15 + 26) + math.log(55)
print(x)
```
也可以在方程的右侧使用数值变量:
```python
x = x + 1
x = math.sin(y) + math.sqrt(0.5 * y)
```
第一行代码将把x的当前值增加1,第二行将给x分配一个值,这个值取决于y的值,例如如果y等于34,x将变成4.65218831173768。
注意,在Python中有一个特殊的快捷方式,允许你在一行代码中定义多个变量:
```python
x, y, z = [1,2,3]
print x # 返回 1
print y # 返回 2
print z # 返回 3
```
### 2.3.2 布尔型
数值变量可以存储一系列不同的数字。布尔变量只能存储两个值,通常称为Yes或No,True或False。很明显,由于布尔变量的范围有限,我们从不使用它来进行计算。我们使用布尔变量来评估条件......还记得吗?
```
你必须身高超过1.5米才能乘坐过山车。
```
"身高超过1.5米"是这个句子中的条件。这个条件要么是真的,要么是假的。你要么比1.5米高,要么比1.5米矮。由于脚本中的大部分流程控制代码都是基于条件语句的,所以布尔值起到了非常重要的作用。让我们来看看一个循环的例子:
```
继续添加牛奶,直到面团可以揉捏。
```
在这条语句条件是:面团必须软到可以揉捏。假设现在我们程序中某些东西(算法)可以评估面团现在的状态。然后第一步我们就会使用这个算法,这样就知道应不应该继续加牛奶了。如果算法返回值是False(即:面团还硬,不可揉捏),我们就继续加奶。加奶以后再一次调用这个算法,直到算法返回True(面团可揉捏啦)。这样我们就知道不需要加更多奶了,可以进行做苹果派的下一个步骤。
在python里从来不使用"0"或"1"或"Yes"或"No"这样的表示方法,对于布尔值,总是使用"True"或"False"。
```python
if curve is None:
print "Something went terribly wrong!"
```
判断语句会返回True或False,只有结果是True(曲线为None值即曲线没有成功添加),才会进入条件判断语句循环打印出"Something went terribly wrong!."的提示语句。
### 2.3.3 字符串
字符串用于存储文本信息。在python里只要给东西两边加上引号,就表示它是字符串。所以如果我们在数字两边加引号,它会变成文本:
```python
variable1 = 5
variable2 = "5"
```
你可以打印到命令行,这2个变量看起来好像一样,但是一旦在算式中使用,就会看出字符串和数字变量的区别:
```python
print (variable1 + variable2) # Results in an "Unsupported Operand Type" Error
```
python抛出一个错误信息,因为我们把字符串变量和整形变量进行了加法运算。要想让运算成功进行,先要把字符串转换为整数。
```python
print (variable1 + int(variable2)) # Results in 10
```
要存储文本信息,除了使用字符串变量别无它法。字符串语法相当简单,但是深入使用字符串涉及到很多小技巧。因为学习刚刚开始,我们只进行简单的操作,比如赋值和连接:
```python
a = "Apfelstrudel" # Apfelstrudel
a = "Apfel" + "strudel" # Apfelstrudel
a = "4" + " " + "Apfelstrudel" # 4 Apfelstrudel
a = "The sqrt of 2.0 = " + str(math.sqrt(2.0)) # The sqrt of 2.0 = 1.4142135623731
```
在软件内部,字符串是以字符序列方式存储。每个字符('char')都来自于Unicode表,表内大约有10万个不同的字符。比如,问号?在unicode表内的索引是63,小写e是101,空格是32:
<div align=center><img src="https://gitee.com/al666ex/developer-rhino3d-com/raw/master/images/primer-strings.svg" width="75%"></div>
<!--TODO: The font in the SVG above is not rendeirng correctly. What Font to use -->
以后我们会用到一些字符串的高级特性,这要求理解字符串的工作方式,但是现在只使用它简单的功能,只要知道它能按你预期的方式工作就行了。
在Rhino python里会重度使用字符串,因为物件ID总是以字符串方式来表示。物件ID就是在物件描述对话框里显示的那一串奇怪的代码: *D7EFCF0A-DB47-427D-9B6B-44EC0670C573*。每个物件的ID被设计为绝对唯一,在整个宇宙中都不会重复,所以在文件中可以用它来安全、清晰的定义一个物件。
### 2.3.4 None变量
每当向Rhino提一些没有答案的问题时,我们需要给Rhino一个方式回答'我不知道'。使用第5页的例子:
```python
curve = rs.AddLine([0,0,0], [somenumber,0,0])
```
并不是100%创建曲线会成功。如果在要求输入长度时用户输入了0,直线的起点和终点就会重合。Rhino并不喜欢长度为0的直线,因此不会向文件中添加这个物件。这意味着`rs.AddLine()`的返回值并不是一个有效的物件ID。在Rhino里几乎所有的方法在失败时会返回一个None值,这样在出错时我们就可以添加错误检查,执行规避操作:
```python
curve = rs.AddLine([0,0,0], [somenumber,0,0])
if not curve:
print "Something went terribly wrong!"
```
python中,在变量x是 *None* ,0或任何空列表时,`if not x`会返回True。
### 2.3.5 使用变量
通常,每当我们在程序里需要使用一个变量,首先要声明这个变量。然而在python中,我们不需要这样做,无需声明,直接创建并使用变量。python也不要求像其他语言那样声明变量的类型。这两点使python成为一个快速且易学的语言。所以,需要使用变量直接写:
```python
a = "Apfelstrudel"
```
当使用变量时,选择一个变量名,并给它赋值(数值、字符串、布尔值等)。变量名需要你自己挑。上面的例子使用的变量名是a,并不是一个很好的选择。首先,它并不能表示这个变量是用来干什么的,然后也不知道它包含的是什么类型的值。比如`strFood`就是一个比较好的名字。前缀str表示这是一个字符串变量,Food的的字面意思显而易见。使用的比较广泛的变量名前缀如下:
<div align=center><img src="https://gitee.com/al666ex/RhinoPython101/raw/master/images/primer-variable-type.svg" width="60%"></div>
看到这么多变量类型不要慌,有些后面的章节会慢慢说,有些你可能再也不会用到。变量的作用域(有的地方叫“生命周期”)指程序中能访问它的区域。第当你在一个函数中声明一个变量,只有在那个函数内部可以读写这个变量。当函数终止时,变量就会超出作用域。生命周期在我看来不是一个好的叫法,因为有的变量能活得很久,但是仍然不能访问到因为它在范围之外。等我们学习函数声明时再来担心这个问题。现在,让我们来看一个正确使用变量的例子:
```python
strComplaint = "I don't like "
strFood = "Apfelstrudel. "
strNag = "Can I go now?"
print(strComplaint + strFood + strNag)
```
这里需要注意一个问题是python的大小写敏感性。和其他语言不同,在python里,"Apfelstrudel", "apfelstrudel" 和 "ApfelStrudel"并不是同一个东西,在程序的其他地方比如变量名、函数、类等这条规则都适用。所以请记住认真对待大小写字母!
现在,再来一个例子。我们使用第2页的宏,但是用变量替换掉一些硬编码数字,以增加灵活性。这程序看起来很恐怖,但是请记住那几行乱七八糟的代码(第10行以后)是因为程序需要模仿宏才那样写的,有点像把阿斯顿-马丁开到人行道上一样。通常情况下,和Rhino沟通不需要使用命令行,那样程序看起来就会友好很多:
```python
import rhinoscriptsyntax as rs
dblMajorRadius = rs.GetReal("Major radius", 10.0, 1.0, 1000.0)
dblMinorRadius = rs.GetReal("Minor radius", 2.0, 0.1, 100.0)
intSides = rs.GetInteger("Number of sides", 6, 3, 20)
strPoint1 = " w" + str(dblMajorRadius) + ",0,0"
strPoint2 = " w" + str(dblMajorRadius + dblMinorRadius) + ",0,0"
rs.Command ("_SelNone")
rs.Command ("_Polygon _NumSides=" + str(intSides) + " w0,0,0" + strPoint1)
rs.Command ("_SelLast")
rs.Command ("-_Properties _Object _Name Rail _Enter _Enter")
rs.Command ("_SelNone")
rs.Command ("_Polygon _NumSides=" + str(intSides) + strPoint1 + strPoint2)
rs.Command ("_SelLast")
rs.Command ("_Rotate3D w0,0,0 w1,0,0 90")
rs.Command ("-_Properties _Object _Name Profile _Enter _Enter")
rs.Command ("_SelNone")
rs.Command ("-_Sweep1 _SelName Rail _SelName Profile _Enter _Enter _Closed=Yes _Enter")
rs.Command ("_SelName Rail")
rs.Command ("_SelName Profile")
rs.Command ("_Delete")
```
|行|描述|
|-------|-------|
|2...5|这里提示用户输入值(实数是双精度浮点数的另一个叫法)。我们给 *rs.GetReal()* 方法提供了4个默认值,一个字符串和3个实数。字符串显示在命令行,第一个实数10.0做为默认选项:<BR><img src="https://gitee.com/al666ex/developer-rhino3d-com/raw/master/images/primer-getrealexample.png" width="75%" margin="10px"><br>同时我们也限制数字在0至1000之间。如果用户输入一个特别大的数,Rhino会说这玩意太大了:<br><img src="https://gitee.com/al666ex/developer-rhino3d-com/raw/master/images/primer-getrealexamplemaximumlimit.png" width="75%">|
|7...8|在这里我们基于`dblMajorRadius` 和 `dblMinorRadius`的值创建字符串。如果用户都选择默认值,那么`dblMajorRadius`会是10.0,`dblMinorRadius`会是2.0,`strPoint2`就是 " w12,0,0"。|
|10...23|这和第3页里的宏是一样的,不同之处在于用变量替换了几处,最后多了3行,删掉了生成实体的结构线(所以我们能多运行程序向次而不崩溃)。|
---
## 下一步
这是python数据结构的基础,接下来学习python的[程序框架](https://gitee.com/al666ex/developer-rhino3d-com/blob/master/_guide_topics/rhinopython/primer-101/3-script-anatomy.md)。