按理说,这篇是收尾了。可能有一点术语,但大都是顾名思义的。重要概念【类】在第二个标题下说明。html
函数式编程(缩写:FP)python
若是对此有兴趣,能够看scheme视频教程(SICP公开课)(scheme是lisp的一种),也能够直接看SICP中译本。我只是“知其大略”罢了。算法
语言若是有如下特征之一,能够被认为有“函数式编程风格”(若是代码使用了这些特征,也说这些代码“有函数式编程风格”):编程
1:函数和数分庭抗礼,毕竟都是用符号表示而已。若是你在中学时对函数有了不错的理解(测试:f(x)什么意思?为何这样写?),那么会很容易明白下面这几行代码在作什么:
数组
def f(x): return 2*x g = f print(g(3))
2:容许用lambda 表达式。这个慢慢会理解的,意思是函数的本质在内容,不在名字,所以操做能够不取名,直接用内容表示。用处主要在,某些函数(好比map)要求你传入一个函数当作参数,你能够不取名(用def定义必须取名)而一会儿传入。在tk中遇到过用lambda强制传参,呃……到时候上网查。ide
3:能够在函数内再定义函数,python中能够这么作,但官方不但愿这么作。函数内部的东西是对外隐藏的,所以若是在A函数内部定义B函数,那么只有在A内部才可使用B函数(变量也同样)。可是Python做为解释性语言,每次调用函数就是把内部语句(包括def)全执行一遍,这很是浪费。咱们仅仅是想表示“B函数只能被A引用”,却要负担如此后果,彷佛不值得。可是并列放置,难免看起来没有层次……(这对强迫症尤为重要)函数式编程
面向对象(缩写:OOP)函数
也是一种风格,和FP不冲突,和FP也有交集。顾名思义,就是关注对象。说某种语言面向对象,主要是说有“类”这么个概念。面向对象也被称为“贴近人的思考过程”,人就是面向对象的(能够先不理解这个说法)。测试
……python的类真的不大好讲。好比要储存一个点,你能够用一个字典(比元组好,由于不须要顺序而且份量有名字),像这样:ui
p1 = {'x' : 3, 'y' : 4} #表示横坐标为3,纵坐标为4的点 print(
类和这很类似,以致于我不懂类的时候,把一个pygame程序中的(简单的)类全用字典表示了。若是用类,能够达到这种效果:(省略了定义类的步骤,因此不要尝试运行)
p1 = Point(3,4) print(p1.x) print(p1.y)
若是你须要好几个点,那么这无疑比字典好得多了。或者遇到这种状况:
a = People('Jack', 7) print(a.name) print(a.age)
这至少看着很优雅。这里,People和Point是“类”,p1和a是对应类的“实例”,建立实例又称“实例化”。我如今补上定义部分,应该很容易懂,至少对于一部分……
class Point: def __init__(self,a,b): self.x = a self.y = b class People: def __init__(self,a,b): self.name = a self.age = b
我一开始也不懂self是啥,并且教程说“能够把self换成任何东西”。你必定但愿写这样的代码,或者假如是这样你就能懂——(你知道init是英文“定义”的缩写)
class Point: def init(a,b): x = a y = b class People: def init(a,b): name = a age = b
实不相瞒,若是不是python,好比C#,那么就是这样子的,并无self 。你彻底就能够把self当成摆设,先用习惯,而后听我下面的解释(我认真的)。
如下两段代码作的事是彻底相同的:
class People: def __init__(self,a,b): self.name = a self.age = b x = People('Jack',7)
class People: pass x = object.__new__(People) x.name = 'Jack' x.age = 7
首先,name和age看似成了“固有属性”,其实压根没有这回事。在默认状况下,python中的类是能够【随时】添加【任意】属性的。你能够在上述(任一)代码段后加上一句x.hello = 1,而后hello就也成了x的属性。对于第二段代码,第一行显然是“建立了一个叫People 的类,但没有内容”,第二行是建立了一个People的对象,但显然它没有属性,毕竟定义类的时候什么都没写,剩下两行给这个对象追加了两个属性。其实你大概已经想到,建立和初始化是两个过程,建立是建立空对象,初始化则添加属性。而添加什么属性,就写在初始化函数里。self你也该明白个大概了,若是我再改一改写法——(先后双下划线表示这个函数是官方的,比较特殊,初始化显然如此)
class People: def self.__init__(a,b): self.name = a self.age = b
若是写成这样,就“差很少是那个意思”了,把self全换成x,就是x的初始化过程。其实还有一重内幕,那就是用点表示属性也是为了好看,真实状况是这样的:
x.__init__(a,b) #实际上是下一行的缩写,相传python真的会如出一辙地处理 type(x).__init__(x,a,b) #type函数返回x的类,等价于下一行 People.__init__(x,a,b)
既然python内部都把__init__当作有三个参数,那咱们定义的时候写三个参数就合乎情理了。思考:假如你不写self,你以为python会怎么错误地【试图】运行你的东西?
简洁模式的People('Jack', 7),其实就是先搞一个空对象,而后(不让你看地)执行了x.__init__('Jack', 7) 。请注意!这个函数的惟一特殊之处就是,会在用普通方法建立对象时被调用。你能够在里面写任何东西,包括print,就像对待一个普通函数——而不是方法。你若是不用类而写一个如出一辙的函数,除了不能用“.”来调用,其余事情彻底相同。甚至你能够建立完了继续调用__init__,把x.__init__('Jack', 7)写多少遍都没问题(能够在修改后用它还原),反正python在建立时执行了一遍就无论了。你能够随便写多少个方法。不写self的后果是,你依然能够用People.f(a,b)调用之,原理就是刚才的过程。
编程风格/范式
能够参考《冒号课堂——编程范式与OOP思想》,不过要注意python没有变量类型,致使没有参数类型,因此自己就是“泛型”;python的一切都是指针(没有变量类型致使), a = 1 意味着在某处建立一个 1,而后把 a 指向那里——而 b = a,表示把 a 储存的位置(这里是那个1的位置)复制给 b ,函数的参数天然本质上也是这些东西,可是这些地址通常都是不可见也不须要可见的;也所以(“不让你看到任何与内存有关的东西”),python不自带数组,python列表和lisp链表是一致的,等等。python不多在意底层算法,python使咱们更容易写出程序结构。
迭代器
试试 a = range(100) 后 print(a) ,你会发现很奇怪的结果。可是list(a) 就是一个从0到99的列表了,a也并不是全无用处,能够用 for i in a取代for i in range(100) 。在老版的python中,range(100)就等于0-99的列表,若是打印就会显示一个列表。可是只是为了重复100遍,彻底不必先创建一个如此长的列表,只须要计数就能够了,这就是迭代器。它很像一个列表,但并无储存每一项,而是每次计算下一项。这是一类专门用于迭代的对象,能够用list()来转换成列表,也能够直接用for循环遍历。除了range返回的对象有这种性质,map等函数也是如此。若是你想获得列表却获得了奇怪的东西(不止一种),那颇有可能这个东西具有上述两个性质,是迭代器。迭代器被称为“很具备python风格的用法”
修饰器(装饰器)
有一种有趣的函数,它的输入是一个函数,输出也是一个函数。这种函数就能够做为修饰器,修饰器有个特殊用法,能够经过在某个def上一行写@后跟修饰器名,就能够把那个函数传入修饰器,并用返回的新函数(“修饰后的函数”)覆盖原有函数。好比这个例子:
def h(func): def func2(x): r = func(x) print("Be called:",func) return r return func2 @h def f(x): return 2*x a = f(3) + 1
另外自行搜索@property,还有知乎回答这个连接解释了@staticmethod 和 @classmethod,我是这个回答下的评论。(我感受本身是对的……)
内置函数
就是python自带的函数,好比print,input等等。
(耐心查看)
最后,你应该知道,python开发人员给咱们留下了一些忠告,虽然不是硬性规定——(运行如下代码)
import this
(完)