当一句或一段代码运行时,Python 解释器将源代码转为字节码,然后进行词法、语法分析,检查是否有错,如果没有错误就编译并执行,然后将结果返回给。这个流程都由 Jupyter Notebook 自动完成。
基本语法 1 2 3 4 5 6 7 8 9 a = 1 a = 1.1 a = "1.1" a = True print("hello" , a) a > b a + b a > b and a < c
可以看到非常直观。在 Python 中,只有一行 print("something.")
也可以看作是一个简单的程序。理论上你的任务都可以由这样一行一行的代码一步步执行下来的,实际上如果是简单任务你也可以这样做。
数据类型 1 2 3 4 a = [] a = set() a = () a = {}
流程控制 循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 a = ['1' , 1 , a, 1 ] for item in a: print(item) a = {'x' :1 , 'y' :2 } for key,value in a.items(): print(key, value) x = [] for i in range(1 , 101 ): x.append(i) y = 0 while y < 10 : y = y+1
条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 x = [] for i in range(1 , 101 ): if i % 2 == 0 : x.append(i) elif i % 3 == 0 : print(i) else : continue y = 0 while y < 10 : y += 1 if y == 5 : break
函数 简单理解,函数就是实现特定(一般是一个)功能的一组代码。
1 2 3 4 5 def my_sum (a, b) : return a+b my_sum(1 ,2 )
前面说一行一行代码语句按步骤执行下来也可以完成任务,但那样就非常不方便,很难复用而且别人看也很不清晰,有了函数就好很多。比如上面的例子,我们可以定义一个加法函数,这样在任何时候直接调用函数就可以了。当我们的任务变得复杂时,这样做的好处就更加明显。
如果涉及到更复杂的任务,比如我们设计一个用户点菜的场景(其实依然非常简单):某用户打开菜单挑选菜品,如果是 VIP 价格显示 8 折;如果菜没了显示售罄,然后提交保存。最 Naive 的实现方式就是完全按业务流程一步步实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 IDENTITY = { "用户1" : "VIP" , "用户2" : "VIP" , "用户3" : "VIP" , "我" : "NORMAL" } MENU = [ {"name" : "回锅肉" , "price" : 32 , "sold_out" : False }, {"name" : "红烧肉" , "price" : 35 , "sold_out" : True }, {"name" : "土豆丝" , "price" : 10 , "sold_out" : False }, {"name" : "水煮肉片" , "price" : 30 , "sold_out" : False }, {"name" : "鱼香肉丝" , "price" : 20 , "sold_out" : False } ] def order (user) : identity = IDENTITY.get(user, "NORMAL" ) if identity == "VIP" : for food in MENU: price = food['price' ] * 0.8 if not food['sold_out' ]: flag = '购买' else : flag = '售罄' print(food['name' ], price, flag) else : for food in MENU: price = food['price' ] if not food['sold_out' ]: flag = '购买' else : flag = '售罄' print(food['name' ], price, flag) order("我" ) order("用户1" )
这样的程序可以执行,但很不灵活,比如增加了一个 VVIP 打 7 折,就需要修改方法;而且打折的比例也是写死在函数里面,很不方便更改,比如今天一个 69 折,明天一个 79 折,这就很麻烦。一般在面对需要判断,有重复代码时,可以考虑下是不是可以抽象。比如我们可以这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def order (user, discount=1.0 ) : identity = IDENTITY.get(user, "NORMAL" ) if identity == "VIP" : print_order(discount) else : print_order(discount) def print_order (discount) : for food in MENU: price = food['price' ] * discount if not food['sold_out' ]: flag = '购买' else : flag = '售罄' print(food['name' ], price, flag) order("我" ) order("用户1" , 0.8 )
这就是函数的最基本功能了,作为新手,我们不需要考虑太多设计模式相关的内容,只需要根据自己的直觉,将一些公共代码抽象出来即可。不过有两点需要强调:
函数尽量小一点,只做一件事
从业务角度思考代码实现
上面的实现虽然做了一些抽象,但从业务角度看其实还有问题,比如我事先并不知道某个用户是什么身份,那我怎么知道 discount 的比例;还有,如果多了个 VVIP,或者其他什么特殊的身份,又要修改代码,添加对应的身份判断。接下来,我们就从这些角度看看怎么优化。
根据身份判断 discount 的问题,我们可以通过维护一个 {身份: discount}
的字典就可以很好地完成了,此时我们判断的条件就不是用户的身份,而是不需要判断,直接拿到了 discount;如果多了个特殊身份,也只需要维护下这个字典即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 IDENTITY_DISCOUNT = { "VIP" : 0.8 , "VVIP" : 0.7 , "NORMAL" : 1.0 } def order (user) : identity = IDENTITY.get(user, "NORMAL" ) discount = IDENTITY_DISCOUNT.get(identity) print_order(discount) def print_order (discount) : for food in MENU: price = food['price' ] * discount if not food['sold_out' ]: flag = '购买' else : flag = '售罄' print(food['name' ], price, flag) order("我" ) order("用户1" )
类 对于绝大多数任务,用函数就足够了(可以创建任意个数量的函数),准确来说,只用函数完全可以实现所有的功能,但有时候为了更好地代码维护和可扩展性,我们会使用类。提到类就不得不提面向对象编程了,它的基本思想是将业务抽象成一个一个对象,每个对象有属性和方法,属性一般是静态的,方法则是可执行的动作。对象是相比函数包含更加丰富的抽象。我们还用上面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class User : def __init__ (self, name) : self.name = name self.identity = IDENTITY.get(name, "NORMAL" ) def order (self) : discount = IDENTITY_DISCOUNT.get(self.identity) print_order(discount) def print_order (discount) : for food in MENU: price = food['price' ] * discount if not food['sold_out' ]: flag = '购买' else : flag = '售罄' print(food['name' ], price, flag) User("我" ).order() User("用户1" ).order()
self
是类的一个实例,每当我们执行 me = User("我")
时,就相当于创建了一个类的实例 me
,就是这个类的一个具体的例子。me
有两个属性:name, identity
和一个方法 order()
。
除了面向对象的编程外,还有一种非常流行的方式:函数式编程。它提供了另外一种解决问题的思路,我们简单地了解下。在这个世界里,没有类,函数是第一等公民。它们大量采用模式匹配、管道和递归,数据在整个过程中保持不可变性(只能创建新的值)。Python 也支持一些函数式操作,接下来我们就看看用这种方式怎么处理:
1 2 3 4 5 6 7 8 9 10 def order (user) : identity = IDENTITY.get(user, "NORMAL" ) discount = IDENTITY_DISCOUNT.get(identity) print_order(discount) def print_order (discount) : print(list(map(lambda food: (food['name' ], food['price' ] * discount, "购买" if not food['sold_out' ] else "售罄" ), MENU))) order("我" ) order("用户1" )
Python 支持的函数式方法比较少,如果使用通道结果看起来会更加直观,比如我们用 elixir
实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 defmodule UserOrder do def order (user) do identity = %{"用户1" => "VIP" , "用户2" => "VIP" , "用户3" => "VIP" , "我" => "NORMAL" } identity_discount = %{"VIP" => 0 .8 , "VVIP" => 0 .7 , "NORMAL" => 1.0 } menu = [ %{:name => "回锅肉" , :price => 32 , :sold_out => false }, %{:name=> "红烧肉" , :price => 35 , :sold_out => true }, %{:name=> "土豆丝" , :price => 10 , :sold_out => false }, %{:name=> "水煮肉片" , :price => 30 , :sold_out => false }, %{:name=> "鱼香肉丝" , :price => 20 , :sold_out => false }] identity_discount |> Map.get(identity |> Map.get(user, "NORMAL" )) |> print_order(menu) end def print_order (discount, menu) do menu |> Enum.reduce([], fn food, res -> item = {food.name, food.price * discount, get_flag(food.sold_out)} [item | res] end ) |> IO.inspect() end defp get_flag (sold_out) do case sold_out do true -> "售罄" false -> "购买" end end end UserOrder.order("我" ) UserOrder.order("用户1" )
代码看起来非常清爽直观。值得说明的是,代码并非越短越好,清晰性和可读性要重要的多。上面的 Elixir 代码和 Python 的函数式方法不需要彻底搞懂,只要大概知道意思即可。最后这里简单对 Python 的几个函数式方法举几个例子介绍下,毕竟属于 Python 的一部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 lst = [1 ,2 ,3 ] tuplst = [("b" , 2 ), ("a" , 1 ), ("c" , 3 )] [i*i for i in lst if i > 1 ] fun = lambda x: str(x) fun(1 ) sorted(tuplst, key=lambda x: x[1 ]) list(map(str, lst)) list(map(lambda x: str(x), lst)) from functools import reducereduce((lambda x, y: x * y), lst) list(filter(lambda x: x > 1 , [1 ,2 ,3 ])) def to_str (lst) : res = [] for i in lst: res.append(str(i)) return res
同样的功能,不同人的观点不同,写出来的代码也不同;即使是同一个人,采用不同的思考方式写出来的代码也不一样。有的代码不仅冗长而且鲁棒性还差,很容易出错;而有的代码不仅简洁优雅鲁棒性好,而且还有很强的可扩展性。我想这可能就是编程的魅力所在吧。
脚本 Python 可以以脚本的形式运行,简单来说就是创建好一个 py
程序,将代码写进去然后在命令行通过 python file_name.py
来执行。比如我们创建一个 order.py
的文件,然后把刚刚的任意一段 Python 代码粘贴进去,在该文件的当前目录执行 python order.py
即可打印出想要的结果。需要注意的是,Python 没有 main 函数,所以单个文件运行时,文件名(__name__
)就是 main,具体使用方法如下:
1 2 3 4 5 6 7 8 9 class User : something def print_order (discount) : something if __name__ == '__main__' : User("我" ).order() User("用户1" ).order()
小结 本节主要介绍了 Python 的基本概念和基本使用,现在是自己动手的时候了。刚开始不需要追求一定要写得多好,先把你想要做的事情实现再说。只要长期坚持写,代码肯定会越写越好的;当有了一定基础后再去看各类书籍才会有茅塞顿开之感。记住,编程首先是个手艺活儿,然后才是个脑力活儿。Talk is cheap, show me the code. Just coding and have fun.