Python编程基础之二语法和内存管理

一、简介

       了解基本Python的语法,有助于更加规范的编写更佳的代码,并结合编程风格便于后期维护。

二、详解

1、Python基本规则和特殊字符

(1)注释#

       井号#表示之后的字符为Python注释。Python注释语句从#字符开始,注释可以在一行的任何地方开始,解释器会忽略掉该行#之后的所有内容。要正确的使用注释。

(2)继续\

       反斜线\继续上一行。Python 语句一般使用换行分隔,也就是说一行一个语句。一行过长的语句可以使用反斜杠\分解成几行。
       有两种例外情况一个语句不使用反斜线也可以跨行。在使用闭合操作符时,单一语句可以跨多行,例如:在含有小括号、中括号、花括号时可以多行书写。另外就是三引号包括下的字符串也可以跨行书写。
        如果要在使用反斜线换行和使用括号元素换行作一个选择,我们推荐使用括号,这样可读性会更好。
(3)多个语句构成代码组:

       缩进相同的一组语句构成一个代码块,我们称之代码组。像 if、while、def和class这样的复合语句,首行以关键字开始,以冒(:)结束,该行之后的一行或多行代码构成代码组。将首行及后面的代码组称为一个子句(clause)。
(4)代码组由不同的缩进分隔
        Python使用缩进来分隔代码组。代码的层次关系是通过同样深度的空格或制表符缩进体现的。同一代码组的代码行必须严格左对齐(左边有同样多的空格或同样多的制表符),如果不严格遵守这个规则,同一组的代码就可能被当成另一个组,甚至会导致语法错误。

        建议:缩进四个空格宽度,避免使用制表符。

        随着缩进深度的增加,代码块的层次也在加深,没有缩进的代码块是最高层次的,不一定是脚本的“main”部分。
       使用缩进对齐这种方式组织代码,不但代码风格优雅,而且也大大提高了代码的可读性。而且它有效的避免了“悬挂 else”(dangling-else)问题,和未写大括号的单一子句问题(如果 C 语言中 if 语句没写大括号,而后面却跟着两个缩近的语句,这会造成不论条件表达式是否成立,第二个语句总会执行)
(5)同一行书写多个语句;

       分号;允许将多个语句写在同一行上,语句之间用分号隔开,而这些语句不能在这行开始一个新的代码块。如import sys; x = 'foo'; sys.stdout.write(x + '\n')必须指出一点同一行上书写多个语句会大大降低代码的可读性,Python 虽然允许但不提倡这么做。

(6)行分隔符\n
换行 (\n) 是标准的行分隔符(通常一个语句一行)。

(7)模块形式组织

       每一个Python脚本文件都可以被当成是一个模块。模块以磁盘文件的形式存在。当一个模块变得过大,并且驱动了太多功能的话,就应该考虑拆一些代码出来另外建一个模块。模块里的代码可以是一段直接执行的脚本,也可以是一堆类似库函数的代码,从而可以被别的模块导入(import)调用。

2、Python赋值

(1)Python 语言中,等号(=)是主要的赋值运算符。从Python 2.0开始,等号可以和一个算术运算符组合在一起,将计算结果重新赋值给左边的变量,这被称为增量赋值(+=、-=、*=、/=、%=、**=、<<=、>>=、&=、^=、|=)。

(2)Python不支持类似 x++ 或 --x 这样的前置/后置自增/自减运算。

(3)多重赋值:将多个对象赋给多个变量,如x = y = z = 1,一个值为 1 的整数对象被创建,该对象的同一个引用被赋值给 x、y 和
z 。

(4)“多元”赋值:将多个变量同时赋值的方法我们称为多元赋值(multuple),这不是官方 Python 术语,而是我们将 "mul-tuple"连在一起自造的。因为采用这种方式赋值时, 等号两边的对象都是元组。如x, y, z = 1, 2, 'a string'。通常元组需要用圆括号括起来,尽管它们是可选的。但建议加上圆括号以使得你的代码有更高的可读性(x, y, z) = (1, 2, 'a string')。
        Python的多元赋值方式可以实现无需中间变量交换两个变量的值。

>>> x, y = 1, 2
>>> x
1
>>> y
2
>>> x, y = y, x
>>> x
2
>>> y
1
显然,Python在赋值之前已经事先对x和y的新值做了计算。

3、Python标识符和关键字

        合法的Python标识符是:第一个字符必须是字母或下划线_;剩下的字符可以是字母和数字或下划线;大小写敏感。

        标识符不能以数字开头,除了下划线符号,其他的符号都不允许使用。处理下划线最简单的方法是把它们当成字母字符。标识符不能关键字

        Python关键字列表和 iskeyword()函数都放入了 keyword模块以便查阅。可以使用import keyword;keyword.kwlist查看。

4、Python专用下划线标识符

        Python用下划线作为变量前缀和后缀指定特殊变量。
_xxx           不能用'from module import *'导入,以单下划线开头的表示的是protected类型的变量,即保护类型只能允许其本身与子类进行访问

__xxx__  系统定义名字,__init__之类的。
__xxx
     双下划线的表示的是类中私有类型的变量。只能是允许这个类本身进行访问了,连子类也不可以
    因为下划线对解释器有特殊的意义,而且是内建标识符所使用的符号,建议程序员避免用下划线作为变量名的开始。一般来讲,变量名__xxx被看作是
私有的,在模块或类外不可以使用。当变量是私有的时候,用__xxx 来表示变量是很好的习惯。变量名__xxx__Python 来说有特殊含义,对于普通的变量应当避免这种命名风格。
      "
单下划线"开始的成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量;
       "
双下划线"开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
    以单下划线开头(_foo)的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用
from xxx import*而导入;以双下划线开头的(__foo)代表类的私有成员;以双下划线开头和结尾的(__foo__)代表python里特殊方法专用的标识,如__init__()代表类的构造函数。

5、Python模块结构和布局

     用模块来合理组织Python代码是简单又自然的方法,一种非常合理的布局:

# (1) 起始行(Unix)
# (2) 模块文档
# (3) 模块导入
# (4) 变量定义
# (5) 类定义
# (6) 函数定义
# (7) 主程序

(1)起始行
        通常只有在类 Unix 环境下才使用起始行,有起始行就能够仅输入脚本名字来执行脚本,无需直接调用解释器。
(2)模块文档
        简要介绍模块的功能及重要全局变量的含义,模块外可通过 module.__doc__ 访问这些内容。
(3)模块导入
       导入当前模块的代码需要的所有模块;每个模块仅导入一次(当前模块被加载时);函数内部的模块导入代码不会被执行,除非该函数正在执行。
(4)变量定义
       这里定义的变量为全局变量,本模块中的所有函数都可直接使用。从好的编程风格角度说,除非必须,否则就要尽量使用局部变量代替全局变量,如果坚持这样做,你的代码就不但容易维护,而且还可以提高性能并节省内存。
(5)类定义语句
       所有的类都需要在这里定义,当模块被导入时 class 语句会被执行,类也就会被定义。类的文档变量是 class.__doc__。
(6)函数定义语句
        此处定义的函数可以通过 module.function()在外部被访问到,当模块被导入时 def 语句会被执行,函数也就都会定义好,函数的文档变量是 function.__doc__。
(7) 主程序
        无论这个模块是被别的模块导入还是作为脚本直接执行,都会执行这部分代码。通常这里不会有太多功能性代码,而是根据执行的模式调用不同的函数。
        注:Python 是否有一种方法能在运行时检测该模块是被导入还是被直接执行呢,__name__ 系统变量便用采用。如果模块是被导入,__name__的值为模块名字;如果模块是被直接执行,__name__的值为’__main__‘。

6、Python内存管理

(1)变量定义

       在Python中,无需事先出现变量声明语句,变量在第一次被赋值时自动声明。而和其他大多数语言一样,变量只有被创建和赋值后才能被使用。
(2)动态类型
       Python中不但变量名无需事先声明,而且也无需类型声明。Python语言中,对象的类型和内存占用都是运行时确定的。尽管代码被编译成字节码,Python仍然是一种解释型语言。在创建--也就是赋值时,解释器会根据语法和右侧的操作数来决定新对象的类型。在对象创建后,一个该对象的应用会被赋值给左侧的变量。
(3)内存分配

        Python 解释器承担了内存管理的复杂任务, 这大大简化了应用程序的编写。只需要关心要解决的问题,至于底层的事情放心交给Python解释器去做就行了。

(4)引用计数

        要保持追踪内存中的对象,Python 使用了引用计数这一简单技术。也就是说 Python 内部记录着所有使用中的对象各有多少引用。一个内部跟踪变量,称为一个引用计数器。至于每个对象各有多少个引用,简称引用计数。当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时,也就是说这个对象的引用计数变为 0 时,它被垃圾回收。
一、增加引用计数:

        当对象被创建并(将其引用)赋值给变量时,该对象的引用计数就被设置为 1。当同一个对象(的引用)又被赋值给其它变量时,或作为参数传递给函数、方法或类实例时,或者被赋值为一个窗口对象的成员时,该对象的一个新的引用,或者称作别名,就被创建(则该对象的引用计数自动加 1)。如x=3.14;y=x。

        语句x=3.14创建了一个浮点数对象并将其引用赋值给x。x是第一个引用, 因此该对象的引用计数被设置为 1。语句 y=x 创建了一个指向同一对象的别名y。事实上并没有为Y创建一个新对象, 而是该对象的引用计数增加了1次(变成了2)。这是对象引用计数增加的方式之一。还有一些其它的方式也能增加对象的引用计数,比如该对象作为参数被函数调用或这个对象被加入到某个容器对象当中时。

二、减少引用计数
        当对象的引用被销毁时,引用计数会减小。最明显的例子就是当引用离开其作用范围时,这种情况最经常出现在函数运行结束时,所有局部变量都被自动销毁,对象的引用计数也就随之减少。当变量被赋值给另外一个对象时,原对象的引用计数也会自动减 1。如foo = 'xyz';bar = foo;foo = 123。当字符串对象"xyz"被创建并赋值给 foo 时, 它的引用计数是 1。当增加了一个别名 bar时,引用计数变成了2。不过当 foo 被重新赋值给整数对象 123 时, xyz 对象的引用计数自动减 1,又重新变成了 1。其它造成对象的引用计数减少的方式包括使用 del 语句删除一个变量,或当一个对象被移出一个窗口对象时(或该容器对象本身的引用计数变成了 0 时)

       一个对象的引用计数在以下情况会减少:

1)一个本地引用离开了其作用范围,如foobar()函数结束时。

2)对象的别名被显式的销毁,如del y   #或del x。

3)对象的一个别名被赋值给其它的对象,如x = 123。

4)对象被从一个窗口对象中移除,如myList.remove(x)。

5)窗口对象本身被销毁,如del myList   #or goes out-of-scope
三、Del 语句
       Del 语句会删除对象的一个引用,它的语法是:del obj1[, obj2[,... objN]],del y会产生两个结果:从现在的名字空间中删除y和x的引用计数减一。执行 del x会删除该对象的最后一个引用,也就是该对象的引用计数会减为0,这会导致该对象从此“无法访问”或“无法抵达”。 从此刻起,该对象就成为垃圾回收机制的回收对象。 注意任何追踪或调试程序会给一个对象增加一个额外的引用, 这会推迟该对象被回收的时间。

7、Python垃圾收集

       不再被使用的内存会被一种称为垃圾收集的机制释放。上面所说的,虽然解释器跟踪对象的引用计数,但垃圾收集器负责释放内存。垃圾收集器是一块独立代码,它用来寻找引用计数为0的对象。它也负责检查那些虽然引用计数大于0但也应该被销毁的对象。 特定情形会导致循环引用。
        一个循环引用发生在当你有至少两个对象互相引用时,也就是说所有的引用都消失时,这些引用仍然存在, 这说明只靠引用计数是不够的。Python的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。当一个对象的引用计数变为 0,解释器会暂停,释放掉这个对象和仅有这个对象可访问(可到达)的其它对象。作为引用计数的补充, 垃圾收集器也会留心被分配的总量很大(及未通过引用计数销毁的那些)的对象。 在这种情况下,解释器会暂停下来, 试图清理所有未引用的循环。

8、Python相关模块

        Python一些模块可以帮助快速开发:Debugger: pdb、Logger: logging和Profilers: profile, hotshot, cProfile。

(1)调试模块pdb允许你设置(条件)断点,代码逐行执行,检查堆栈。它还支持事后调试。

(2)logging模块是在Python2.3中新增的,它定义了一些函数和类帮助你的程序实现灵活的日志系统。共有五级日志级别: 紧急、 错误、警告、信息和调试。

(3)Python也有好几个性能测试模块。 最早的Python profile模块是 Python 写成的,用来测试函数的执行时间,及每次脚本执行的总时间,既没有特定函数的执行时间也没有被包含的子函数调用时间。在三个profile模块中,它是最老的也是最慢的,尽管如此它仍然可以提供一些有价值的性能信息。hotshot模块是在Python2.2中新增的,它的目标是取代 profile 模块,它修复了profile模块的一些错误,因为它是用 C 语言写成,所以它有效的提高了性能。 注意hotshot重点解决了性能测试过载的问题,但却需要更多的时间来生成结果。Python2.5 版修复了hotshot 模块的一个关于时间计量的严重 bug。cProfile模块是Python2.5新增的,它用来替换掉已经有历史的hotshot和profile模块。被作者确认的它的一个较明显的缺点是它需要花较长时间从日志文件中载入分析结果,不支持子函数状态细节及某些结果不准,它也是用 C 语言来实现的。

三、总结

(1)变量占用的内存是如何分配和回收的,对程序员合理使用内存非常重要,可结合实例进行深入分析。
(2)Python不断在完善和更新,一些新的特性也须不断的体验。
(3)若有不足,请留言,在此先感谢!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乌托邦2号

博文不易,支持的请给予小小打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值