Python学习笔记

这篇内容其实很早就在自己的电脑上整理完成了,主要用作对做记录整理Python和其他语言不太一样的地方,使自己可以快速回顾Python知识,使用Python。如果你学习过Python但平时不常用Python,或许也可以试着参考一下。(内容是由简入深的,但是现在看来显得有些混乱,不够系统

内容摘要

  1. 语言基本内容(语句、数组等
  2. 面向对象
  3. Python魔术方法
  4. 生命周期
  5. 正则
  6. 其他内容(Python之禅、打包等

语言基本内容

运算符:
  1. **即求平方。
  2. //地板除即去掉小数部分。
  3. /传统除法,整数地板除,浮点数精确除。
字符串:
  1. 三引号,多行文本,可以不用写\n
  2. 可以使用+=
  3. 字符串可以和int类型相乘,但不能相加
  4. 可以比较,单字母按顺序比,如'abc'<'b'结果为True
类型转换:
  • 使用int(value)来将value转换为int
变量:
  1. 变量可以直接使用,不用声明,并且可以重复赋值。可以使用del 删除变量
  2. 命名:
    1. 单下划线开头表示模块私有,使用import导入时不会包含
    2. 双下划线开头表示类内私有
    3. 类和异常使用Pascal风格(单词首字母大写)
    4. 常量使用全大写加下划线
    5. 其他即使是模块名也使用全小写加下划线
类型:
  1. Boolean:TrueFalse,首字母大写
语句:
  1. 控制语句后需要加:,代码块使用Tab缩进进行分割
  2. 逻辑运算符,和其他语言不一样了。
    1. and
    2. or
    3. not取反,注意Python是有!=这个比较符的
  3. 运算符优先级问题,请直接加()
List
  1. 使用[,,,,]形式创建,数组没有固定的类型,元素可以是任何类型
  2. 字符串可以看作不能改变的字符数组
  3. 数组可以使用+*
  4. 可以使用in来判断数组中是否包含否个元素,'a' in 'array'返回True
  5. 使用.append(item)添加元素
  6. 使用len(array)获取array的长度
  7. 使用array.insert(index,item)array的指定index位置插入item
  8. 使用array.index(item)获取指定元素第一次出现的索引,未找到则触发ValueError
Range,用来创建顺序的数字列表
  1. range(10),从0到指定数字,相当与range(0,10,1)
  2. range(3,8)[3,4,5,6,7],从3开始,不包含8
  3. range(3,8,3),[3,6],从3开始,每隔3个,不包含8
  4. 使用list(range),可以将range转为数组
for
  1. for item in items:形式,items可以是数组、range
  2. switch,可以将case条目和对应的方法构建一个字典,自行实现switch
方法
  1. 方法的定义是统一的 def fun_name():,有返回值也不用声明,方法体内写return即可。
  2. 方法可以被赋值给一个普通变量,比如a,之后可以使用a()调用
  3. 方法也可以作为参数传递。
注释
  1. #
  2. 多行注释,没有,请用多个#
  3. 文档注释,在方法声明下面使用三引号括住。如下:
    def add(x,y):
        """
        加法。
        """
        print(x+y)
    
模块:
  1. from math import pi,sqrt
  2. import pi from math
  3. import pi from math as PI
标准库:
  1. 有用python写的,有用C写的。大部分都是跨平台的,也有Windows和Unix专用的
  2. PyPI(Python Package Index)存放了各种第三方模块,是用它们最好的办法是通过pip
异常:
  1. 类型
    • ImportError
    • IndexError
    • NameError
    • SyntaxError
    • TypeError
    • ValueError
  2. 捕获
    • try...except...
    • try...except(ValueError,TypeError)
    • 同样支持finally
  3. 抛出异常 raise ValueError('Detail')
  4. 可以通过assert (5 >= 0), "Colder than absolute zero!"进行一次断言,如果出错会抛出AssertionError
    try:
    num = 5 / 0
    except:
    print("An error occurred")
    raise
    
文件:
  1. 打开文件,file=open(path)
    1. open('path')相当于open('path','r')默认只读
    2. open('path','w'),只写
    3. open('path','a'),追加模式
    4. open('path','wb'),在后面加上一个b意味着以二进制模式打开,常用于图像和音频。
    5. +r+可读可写;w+可读可写,没有就创建;a+可读可写,没有就创建,指针在文件末尾;同样可以在后边加上b
  2. 关闭文件,file.close(),在操作系统级别对打开的文件数目有限制。
  3. 读取文件,content=file.read()
    1. file.read(16)读16个字节的内容
    2. 继续调用file.read(11),会接着之前的继续读
    3. 如果没有可读内容后,会返回一个空字符串。
    4. 可以调用file.readlines(),改方法会返回一个字符串数组。
  4. 写入文件,file.write('str'),如果写入成功,返回写入了多少字节。
    1. 写入后需要调用file.close(),才能保存更改。
  5. 最佳实践,使用with
    try:
        with open("path", "a") as file:
            file.write('Hello World')
    except:
        raise
    
更多类型:
  1. None,类似其他语言的null
    1. bool(None)返回False
    2. print(None)输出None
    3. 没有返回值的方法返回None
  2. Dictionaries,字典
    1. {'key1':value,'key2':value}
    2. 使用dict['key']访问元素,如果试图访问一个不存在的元素,会抛出KeyError
    3. 只有不可变对象可以用作key,因为可变对象不可被hash
    4. 方法
      1. 使用dict['key']=value来添加一个键值对
      2. 使用in或者not in判断一个key是否存在于字典内。
      3. 使用get可以类似索引一样访问,只是能够返回一个默认值。print(pairs.get(12345, "not in dictionary"))
  3. Tuples,元组,不可变版本的List,使用()创建。
    1. 元组本身不可变,但如果他有一个可变的条目比如list,该list的内容是可变的。
    2. 也可以不用(),比如my_tuple = "one", "two", "three"
  4. Sets,集合
    1. 使用{1,2,3,4,5}set(list)创建
    2. 不能使用{}创建,{}用于创建字典。创建空的集合可以使用set()
    3. 可以使用innot in检查元素
    4. 无序的,不能被索引。
    5. 不能包含双重的。
    6. 比list快
    7. 不使用append使用addremove移除特定元素,pop删除任意元素。
    8. 操作符
    first = {1, 2, 3, 4, 5, 6}
    second = {4, 5, 6, 7, 8, 9}
    
    print(first | second)
    print(first & second)
    print(first - second)
    print(second - first)
    print(first ^ second)
    
    >>>
    {1, 2, 3, 4, 5, 6, 7, 8, 9}
    {4, 5, 6}
    {1, 2, 3}
    {8, 9, 7}
    {1, 2, 3, 7, 8, 9}
    >>>
    
  5. Generators
    1. 也是iterable的一种,不能使用下标访问,可以使用for循环遍历。
    2. 使用方法和yield创建。方法每一次都执行到yield,并返回后跟的值
    3. 因为它是不断运行不断的生成值并返回,所以即使是无限大的序列也是可以的,不会占用太多内存。
    4. 有限的Generators可以通过list()转成一个list
    def countdown(x):
        while(x>=0):
            yield x
            x-=1
    
    for x in countdown(10):
        print(x)
    
  6. Decorators
    1. 可以去修改一个方法,类似代理模式。
    def decor(func):
        def wrap():
            print("============")
            func()
            print("============")
        return wrap
    
    def decor1(func):
        def wrap1():
            print("**************")
            func()
            print("**************")
        return wrap1
    
    @decor
    @decor1
    def print_text():
        print("Hello world!")
    
    print_text();
    
    the outcome:
    ===========
    **************
    Hello world!
    **************
    ===========
    
  7. Recursion递归,用于自引用的解决相同类型的更小的子问题。
    1. 需要一个退出条件。
    2. 互相调用也可以称为递归。
List切片
  1. list[3:7],返回一个新的list包含原list的第3到第6项,第一个省略,则是从零;第二个省略,则是终点。
  2. 切片还可以有第三个参数,间隔\步长
  3. 可以使用负数,当前两个参数使用负数时,表示倒数第几个;当第三个参数使用负数时,表示反向。比如,可以使用list[::-1]来反转一个list。
List推导
  1. cubes = [i**3 for i in range(5)]返回[0, 1, 8, 27, 64]
  2. 还可以使用if来使值满足一定的条件,比如evens=[i**2 for i in range(10) if i**2 % 2 == 0]
  3. 如果一次创建过大的list会引发MemoryError,比如even = [2*i for i in range(10**100)]
字符串格式化
  1. 使用位置参数,print("{0},{0},{0}".format(1,2,3))输出1,1,1
  2. 也可以使用指定参数,print("{x},{0},{y}".format(1,x=1,y=2))输出1,1,2
常用的字符串方法:
  1. join,用一个字符作为分隔符连接一个list里的字符串,", ".join(["spam", "eggs", "ham"])
  2. replace,替换字符串中的某个字符串,"Hello ME".replace("ME", "world"),返回Hello world
  3. startswithendswith
  4. lower``upper
  5. split,与join相反
常用的数学方法:
  1. max`min`
  2. abs绝对值
  3. sum
  4. round四舍五入
常用的数组方法:
  1. all`any`,判断
  2. enumerate,可以用来同时对一个list的索引和值进行迭代
函数式编程
  1. 高阶方法:把其他的方法作为参数或者返回值
  2. 纯函数式:输入一定,输出一定,不依赖其他状态。复用性好,逻辑清楚。
Lambda
  1. 语法:lambda arg1,arg2:statementlambda后跟参数列表,然后是一个:,最后是表达式
  2. 很方便的创建匿名函数,也可以将其分配给一个变量。
常用的内建高阶函数:mapfilter
  1. map(func,iterables),对iterables的每一项执行func()操作。函数返回值也是一个iterables,所以我们常用list(result)将其转化为list
  2. filter(determine-func,iterables),对iterables的每一项执行func()保留返回True的项目。返回值同上
itertools
  1. 一个标准库包含多种有用的操作
  2. count(n)从n开始产生无限的序列
  3. list(repeat("Huri", 5))
  4. cycle("Huri")
  5. takewhile\chain\accumulate
    from itertools import accumulate, takewhile
    
    nums = list(accumulate(range(8)))
    print(nums)
    print(list(takewhile(lambda x: x<= 6, nums)))
    
    >>>
    [0, 1, 3, 6, 10, 15, 21, 28]
    [0, 1, 3, 6]
    >>>
    
    1. takewhilefliter的区别首先是takewhile只要遇到一个不符合的,就直接返回前边的,不再对后面的进行判断。
    2. chain的参数是两个iterable
    3. accumulate是对每一个都加上(前面所有的和)
  6. product and permutation
    from itertools import product, permutations
    
    letters = ("A", "B")
    print(list(product(letters, range(2))))
    print(list(permutations(letters)))
    
     >>>
    [('A', 0), ('A', 1), ('B', 0), ('B', 1)]
    [('A', 'B'), ('B', 'A')]
    >>>
    

面向对象

封装:
  1. class Dog
  2. 所有方法声明,第一个参数都需要指向实例。从第二个参数开始才能指向真实的参数。包括构造器方法。
  3. 构造器:__init__,只能没有或有一个。可以在里面声明实例属性。
  4. 在构造方法外声明的属性是类属性,也可以在写代码的时候直接写,类名后直接写一个属性,就是类属性,实例后跟一个属性就是实例属性。
  5. 试图访问一个没有的属性或方法会引发异常AttributeError:
继承和多态:
  1. class Dog(Animal):
  2. 如果重写了父类的方法,则会调用自己的方法。
    1. 注意,如果子类和父类方法名一致,但参数不一致,即使子类实例调用该方法的方法参数和父类一致,子类实例还是会调用子类的方法,然后报参数错误。
  3. 调用父类的某个方法super().spam()

魔术方法

是在开头和结尾都有双下划线的方法。

  1. 操作符重载
    1. def __add__(self, other):
      1. 如果a实现了__add__a+b->a.__add__(b)
      2. 如果a没实现,a+b->b._radd__(a)
      3. 都没实现,则TypeError: unsupported operand type(s) for +:
    2. 计算操作表
      __sub__ for -
      __mul__ for *
      __truediv__ for /
      __floordiv__ for //
      __mod__ for %
      __pow__ for **
      __and__ for &
      __xor__ for ^
      __or__ for |
      
      1. 这些方法都和上面的+逻辑一样,也都有对应的r方法
    3. 对比操作表
      __lt__ for <
      __le__ for <=
      __eq__ for ==
      __ne__ for !=
      __gt__ for >
      __ge__ for >=
      
      1. 如果__ne__没有实现,会执行__eq__并取反返回
    4. 常用魔术方法
      __len__ for len()
      __getitem__ for indexing
      __setitem__ for assigning to indexed values
      __delitem__ for deleting indexed values
      __iter__ for iteration over objects (e.g., in for loops)
      __contains__ for in
      
    5. 不常用魔术方法
      __call__ 方法调用
      __int__ for int()
      __str__ for str()
      

生命周期

对象实例的创建、操作和销毁

  1. 对象的定义

  2. 实例初始化,__init__被调用,这时对象内存就分配了。如果有__new__的话,就不会调用__init__

    1. __new__是用创建对象的
    2. __init__用来初始化的
  3. 实例删除,__del__方法,引用计数-1,分配名字或放置到容器中时引用计数+1,当引用计数为0或只有互相引用时,内存被释放。

  4. 类方法使用@classmethod修饰

    class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def calculate_area(self):
        return self.width * self.height
    
    @classmethod
    def new_square(cls, side_length):
        return cls(side_length, side_length)
    
    square = Rectangle.new_square(5)
    print(square.calculate_area())
    
    >>>
    25
    >>>
    
  5. 静态方法,staticmethod修饰,不需要第一个参数

    class Pizza:
    def __init__(self, toppings):
        self.toppings = toppings
    
    @staticmethod
    def validate_topping(topping):
        if topping == "pineapple":
        raise ValueError("No pineapples!")
        else:
        return True
    
    ingredients = ["cheese", "onions", "spam"]
    if all(Pizza.validate_topping(i) for i in ingredients):
    pizza = Pizza(ingredients)
    
  6. 属性,``修饰,常用于使属性变为只读

    class Pizza:
    def __init__(self, toppings):
        self.toppings = toppings
    
    @property
    def pineapple_allowed(self):
        return False
    
    pizza = Pizza(["cheese", "tomato"])
    print(pizza.pineapple_allowed)
    pizza.pineapple_allowed = True
    
  7. 属性的settergetter

    class Pizza:
    def __init__(self, toppings):
        self.toppings = toppings
        self._pineapple_allowed = False
    
    @property
    def pineapple_allowed(self):
        return self._pineapple_allowed
    
    @pineapple_allowed.setter
    def pineapple_allowed(self, value):
        if value:
        password = input("Enter the password: ")
        if password == "Sw0rdf1sh!":
            self._pineapple_allowed = value
        else:
            raise ValueError("Alert! Intruder!")
    
    pizza = Pizza(["cheese", "tomato"])
    print(pizza.pineapple_allowed)
    pizza.pineapple_allowed = True
    print(pizza.pineapple_allowed)
    
    >>>
    False
    Enter the password to permit pineapple: Sw0rdf1sh!
    True
    >>>
    

正则

目的

  1. 验证字符串是否匹配
  2. 在一个字符串中进行替换

Python中的使用:

  1. re模块
  2. 使用re.match匹配,匹配返回表示匹配的对象,否则返回None
  3. 使用r不会转义任何字符串内容。

方法

  1. re.match
    1. 从开头开始匹配
  2. re.search
    1. 从任意地方匹配
  3. re.findall
    1. 返回一个字符串list
  4. re.sub(pattern, repl, string, max=0)替换

正则匹配对象的方法

import re

pattern = r"pam"

match = re.search(pattern, "eggspamsausage")
if match:
   print(match.group())
   print(match.start())
   print(match.end())
   print(match.span())

>>>
pam
4
7
(4, 7)
>>>

Groups

  1. group(0)group()返回整个匹配对象
  2. group(n)返回从1开始数,按(出现的位置
  3. groups()返回除了geoup(0)以外所有的匹配的list
import re

pattern = r"a(bc)(de)(f(g)h)i"

match = re.match(pattern, "abcdefghijklmnop")
if match:
   print(match.group())
   print(match.group(0))
   print(match.group(1))
   print(match.group(2))
   print(match.groups())

>>>
abcdefghi
abcdefghi
bc
de
('bc', 'de', 'fgh', 'g')
>>>

特殊的group

  1. 命名group(?P...)
  2. 不捕获group(?:...)
import re

pattern = r"(?Pabc)(?:def)(ghi)"

match = re.match(pattern, "abcdefghi")
if match:
   print(match.group("first"))
   print(match.groups())

>>>
abc
('abc', 'ghi')
>>>

特殊序列:反斜杠和1到99之间的数字,例如\1\17。这匹配该数字的组的表达式。

import re

pattern = r"(.+) \1"

match = re.match(pattern, "word word")
if match:
   print ("Match 1")

match = re.match(pattern, "?! ?!")
if match:
   print ("Match 2")

match = re.match(pattern, "abc cde")
if match:
   print ("Match 3")

>>>
Match 1
Match 2
>>>

正则元字符

  1. .,除了新行之外的所有字符
  2. ^$,匹配开始和结尾
  3. []匹配方括号中的一个
  4. [a-z]-表示范围
  5. [^a-z]^表示非
  6. *``+``?``{}?相当于{0,1}
  7. \d匹配数字,\s匹配空格,\w字符(),大写表示相反
  8. \b字符边界

其他内容

Python之禅

使用import this运行,控制台会输出下面的内容

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Python之禅有20条原则,这里面只提到了19条,第二十条是使用空白 :)

PEP

Python Enhancement Proposals (PEP)是经验丰富的Python开发者的建议,PEP8作为一个风格指南有很多条规则。

除了本文一开始的命名规范,主要还有这些内容:

  1. 一行不应该超过80个字符
  2. 不要使用from module import *
  3. 每行只应该有一条语句
方法参数
  1. 可变参数*args,方法内使用元组访问,必须在命名参数之后,args不是规定的名字。
    def function(named_arg, *args):
        print(named_arg)
        print(args)
    
    function(1, 2, 3, 4, 5)
    
    >>>
    1
    (2, 3, 4, 5)
    >>>
    
  2. 默认参数。命名参数可以有一个默认值,必须在没有默认值的命名参数之后
    def function(x, y, food="spam"):
        print(food)
    
    function(1, 2)
    function(3, 4, "egg")
    
    >>>
    spam
    egg
    >>>
    
  3. 关键词参数,**kwargs可以用于处理你没有定义的命名参数,以字典的形式传入。放在最后面
    def my_func(x, y=7, *args, **kwargs):
        print(kwargs)
    
    my_func(2, 3, 4, 5, 6, a=7, b=8)
    
    >>>
    {'a': 7, 'b': 8}
    >>>
    
元组解包

将一个iterable(通常是Tuple)的每一项赋值给一个变量,以*开头的变量会取出其他剩余的值,保证成一个list

a, b, *c, d = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(a)
print(b)
print(c)
print(d)

>>>
1
2
[3, 4, 5, 6, 7, 8]
9
>>>
三元运算符 b = (value) if (statement) else (another-value)
else

else可以配合for使用,当for循环正常结束时会调用else的语句,如果break则不会调用该语句。

for i in range(10):
   if i == 999:
      break
else:
   print("Unbroken 1")

for i in range(10):
   if i == 5:
      break
else:
   print("Unbroken 2")


>>>
Unbroken 1
>>>

else也可以配合try...except...使用,只有在try正常执行完毕时才会调用else

try:
   print(1)
except ZeroDivisionError:
   print(2)
else:
   print(3)

try:
   print(1/0)
except ZeroDivisionError:
   print(4)
else:
   print(5)


>>>
1
3
4
>>>
__main__

被当作模块导入时,名字不会是__main__。如果Python解释器正在将该模块(源文件)作为主程序运行,则会将特殊的__name__变量设置为__main__。如果这个文件是从另一个模块导入的,__name__将被设置为模块的名字。

def function():
   print("This is a module function")

if __name__=="__main__":
   print("This is a script")

>>>
This is a script
>>>

主要的三方库

  1. 流行的Web框架,Django(Instagram和Disqus都在使用)、CherryPy和Flask
  2. 抓包,BeautifulSoup
  3. matplotlib允许你创建基于Python数据的图表
  4. NumPy,极快的多维数组和数学运算。
  5. SciPy包含许多对NumPy的扩展
  6. 用于3D游戏的Panda3D,用于2D游戏的pygame

打包

将模块写成标准格式,使其他程序员可以轻松安装和使用它们。需要用到setuptoolsdistutils两个模块

  1. 第一步是正确组织现有文件,将所有要放入库的文件放在同一个父目录中。该目录还应该包含一个名为__init__.py的文件,该文件可以是空白的,但必须存在于目录中。
  2. 把这个目录放到另一个包含自述文件和许可证的目录,以及一个名为setup.py的重要文件。最后的结构如下:
    SoloLearn/
        LICENSE.txt
        README.txt
        setup.py
        sololearn/
            __init__.py
            sololearn.py
            sololearn2.py
    
  3. setup.py,包含必要的信息,如名字、版本等
    from distutils.core import setup
    
    setup(
        name='SoloLearn',
        version='0.1dev',
        packages=['sololearn',],
        license='MIT',
        long_description=open('README.txt').read(),
    )
    
  4. 上传到PyPI,分为源代码发行版和二进制发行版。
  5. 在目录里可以使用python setup.py install安装
打包成可执行文件

考虑有些普通用户没有装Python,所以直接打包成对应平台的可执行文件。

  1. Windows
    1. py2exe
    2. PyInstaller
    3. cx_Freeze
  2. Mac
    1. py2app
    2. PyInstaller
    3. cx_Freeze

你可能感兴趣的:(Python学习笔记)