标签:for循环 search title 个性 简单 run 其他 range 课堂
按理说,这篇是收尾了。可能有一点术语,但大都是顾名思义的。重要概念【类】在第二个标题下说明。
函数式编程(缩写:FP)
如果对此有兴趣,可以看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强制传参,呃……到时候上网查。
3:可以在函数内再定义函数,python中可以这么做,但官方不希望这么做。函数内部的东西是对外隐藏的,因此如果在A函数内部定义B函数,那么只有在A内部才可以使用B函数(变量也一样)。但是Python作为解释性语言,每次调用函数就是把内部语句(包括def)全执行一遍,这非常浪费。我们仅仅是想表示“B函数只能被A引用”,却要负担如此后果,似乎不值得。但是并列放置,不免看起来没有层次……(这对强迫症尤其重要)
面向对象(缩写:OOP)
也是一种风格,和FP不冲突,和FP也有交集。顾名思义,就是关注对象。说某种语言面向对象,主要是说有“类”这么个概念。面向对象也被称为“贴近人的思考过程”,人就是面向对象的(可以先不理解这个说法)。
……python的类真的不大好讲。比如要储存一个点,你可以用一个字典(比元组好,因为不需要顺序并且分量有名字),像这样:
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
(完)
标签:for循环 search title 个性 简单 run 其他 range 课堂
原文地址:http://blog.51cto.com/13535617/2065633