Python基础手记

Python是一门开箱即用的动态语言,结合搜索引擎和cv大法其威力不可小觑。但很多人(包括我)的Python写的像Java或者C++,他们知道如何用Python,却不知道如何Pythonic地写Python。本文不赘述常识性的Python或编程基础,聚焦于Python特有的,和其他语言不太一样的部分,无法面面俱到还请原谅。

本文基于Python3.7

  1. 格式化输出
first = 'kevin'
last = 'xue'
full = f'{len(first)} {last}'  # f或者F表示这是一个格式化字符串,通过计算得出
print(full) # 大括号内可以是任意表达式
  1. 字符串函数
course = '  python  '
print(course.upper())
print(course.lower())
print(course.title())  # 首字母大写
print(course.strip())
print(course.find('p'))
print(course.replace('p', 'j'))
print('py' in course)
print('swift' not in course)
  1. Number类型函数
import math  # 该模块内置很多代数函数
print(round(2.6))  # 四舍五入
print(abs(-2.9))

print(math.ceil(2.2))  # 向上取整
  1. 三元运算符
val = 2 if 3 > 2 else 1
print(val)
  1. Pyhon中的逻辑运算符
    Python中的逻辑运算符不再是&&,||,!而是and,or和not:
if 3 > 2 and 2 > 1:
    print('yes')

if 3 > 2 or 5 > 6:
    print('yes')

if not 3 > 6:
    print('yes')
  1. 神奇的链式比较
    Python提供了一种更加代数友好更加优雅的比较表达式:
x = 6
if 3 < x < 7:  # 等价于 x>3 and x<7
    print('yes')

是不是很漂亮?

  1. Python的类型转换
    Python是一门强类型语言,意味着变量的类型确定之后很多操作会受到限制
    比如Python中不能把字符串和int相加,也不能大小比较
    同时,Python中基本没有隐式类型转换,很多操作之前需要进行强制类型转换
x = int(input('x:'))  
y = x+1
print(f"y:{y}")
  1. Python独有的For..else语法糖
for item in container:
    if search_something(item):
        # Found it!
        process(item)
        break
else:
    # Didn't find anything..
    not_found_in_container()

值得注意的是,break的存在是必要的,否则此处的else将没有任何意义

  1. 关于None
    Python中并没有null,取而代之的是None
    所有的函数默认返回None,除非你显式return
  2. 默认参数
def increment(number, by=1): #默认参数要放在位置参数后面
   return number + by


print(increment(2))
  1. 可变参数
def multiply(*numbers):
    total = 1
    for num in numbers:
        total *= num
    return total


print(multiply(2, 3, 4, 5))
  1. 关键字参数
    可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
def save_user(**user):
    print(user)


save_user(id=1, name='kevin', age=22)  #{'id': 1, 'name': 'kevin', 'age': 22}
  1. 尽量不要使用全局变量,更不要试图在函数中通过global修改全局变量
  2. 列表list
    Python最重要的数据结构之一
letters = ['a', 'b', 'c']
matrix = [[0, 1], [2, 3]]
zeros = [0]*5
combined = zeros+letters
numbers = list(range(10))
chars = list("kevin")
chars[::-1]   #倒序
  1. 列表/元组拆箱(list/tuple unpack)
numbers = [1, 2, 3]
first, second, third = numbers  # 变量数量需要等于列表长度
first, second, *others = numbers # 多余的元素会被放置到others列表

# 等价于
first = numbers[0]
second = numbers[1]
third = numbers[2]
  1. list遍历和增删改查
letters = ['a', 'b', 'c', 'd']

for letter in letters:
    print(letter)

for index, letter in enumerate(letters):  # 需要序号
    print(f"{index}:{letter}")

# Add
letters.append('e')
letters.insert(0, '-')
print(letters)

# Remove
letters.pop()  # 删除末尾元素
letters.pop(0)
letters.remove('b')  # 删除第一个'b'
del letters[0:3] # 批量删除元素
letters.clear()

# Find
letters.count('a')
if 'd' in letters:
    print(letters.index('d'))  # 不存在的元素不会返回-1,而是直接报错

  1. list排序
l = [2, 3, 1, 10, 4, 5, 6, 7]
l.sort(reverse=True)  # 简单list的排序
sorted(l, reverse=True)  # 不会改变原list,返回排好序的副本

# 复杂list排序
items = [
    ('product1', 20),
    ('product2', 22),
    ('product3', 43),
    ('product4', 10),
]


def item_key(item):
    return item[1]


items.sort(key=item_key, reverse=True)
items.sort(key=lambda item: item[1], reverse=True) # 通过lambada匿名函数改写
  1. list过滤和映射
items = [
    ('product1', 20),
    ('product2', 22),
    ('product3', 43),
    ('product4', 10),
]


x = list(map(lambda x: x[1], items))
y = list(filter(lambda x: x[1] >= 20, items))
  1. 列表推导式(list comprehension)
    无疑是Python强大的特性之一
z = [item[1] for item in items if item[1] >= 20]
  1. zip打包函数
x = [1, 2, 3]
y = ['a', 'b', 'c']
print(list(zip(x, y))) # [(1, 'a'), (2, 'b'), (3, 'c')]
  1. tuple和list的操作非常相似,不同之处在于tuple是不可变的:
t = 1, 2  # 不需要括号也是tuple
x = 10
y = 11
x, y = y, x  # 利用tuple交换两个变量的值
a, b = 0, 1  # 利用tuple赋值
  1. 当处理大量数字时,array比list在性能的表现上更优秀,如果不考虑性能则无脑list即可
from array import array

numbers = array('i', [1, 2, 3])  # 所有元素类型需要一致
  1. 集合(set)
numbers = [1, 1, 2, 2, 4, 3]
first = set(numbers)
second = {1, 5}

print(first | second)  # 并集
print(first & second)  # 交集
print(first - second)  # 差集
  1. 字典(dictionary)
    key只能是不可变类型,值得注意的是,set和dict都是无序的,意味着它们不能随机访问也不能排序

point = {'x': 1, 'y': 2, 'z': 4}
point = dict(x=1, y=2, z=4)  # 构造dict的方法

if 'a' in point:
    print(point['a'])  # 访问不存在的key会报错

print(point.get('a'), 0)  # 不存在的key返回0,默认返回None

del point['x']

for key, val in point.items():  # 返回tuple,利用拆箱赋值
    print(key, val)
  1. 再探推导式(comprehension expression)
    推导式对dict和set同样起作用
nums = {x for x in range(10)}
vals = {x: x * 2 for x in range(5)}
  1. 生成器(generator)
    使用列表推导式时,会一次性生成整个数据序列,当序列非常大甚至是无穷尽的时候,我们需要生成器,它是iterable的,同时数据会在被需要时动态生成,有点的感觉,不是吗?
vals = (x*2 for x in range(10)) # 使用圆括号包围推导式时,我们得到一个generator
for item in vals:
    print(item)
  1. 拆箱操作符(unpacking operator)
    又一个强大的Python特性
    所谓拆箱的意思就是把任意iterable拆分成多个其内元素

l = [*range(10), 'w', *'kevin']
print(*l)  # 通过拆箱传入多个可选参数
x = {'x': 10, 'y': 20}
y = {'z': 30}
z = {**x, **y}  # dict也可以拆箱

  1. 异常处理
    简单来说异常处理就是捕获程序可能抛出的甚至很多时候不可避免的异常,给用户提供错误信息的同时不打断程序执行流,由于机制与Java等语言大同小异,此处不赘述
try:
    age = int(input("age:"))
except (ValueError, ZeroDivisionError) as error: # 同时捕获多种异常
    print('you didn\'t enter a valid age.')
    print(error)
else:  # optional 如果没有异常则执行
    print('no exceptions were thrown')
finally: # 总会执行
    pass
  1. with语句
    通过with打开外部资源,会自动释放资源
with open('app.py') as f:
    f.read()
  1. 抛出异常(raise exception)
    一般不建议手动抛出异常,时间花费较大
def calculate(age):
    if age <= 0:
        raise ValueError('age cannot be 0 or less')
    return 10/age
  1. 类名的一般命名规则:驼峰命名首字母大写
class Point:
    default_color = "red"  # 类属性

    def __init__(self, x, y):  # 构造器:需要显式定义self
        self.x = x
        self.y = y

    def draw(self):
        print(f"Point ({self.x},{self.y})")

    def __str__(self): # 用于将对象转换为字符串
        return f"Point ({self.x},{self.y})"


point = Point(1, 2)
point.z = 10  # Python可以动态添加实例属性
print(point)
print(type(point))
print(isinstance(point, Point))  # 判断对象的归属类

形如__init__和__str__这种有双下划线的函数,称为魔法方法(magic method),它们一般由Python解释器在合适的时间自动调用

  1. 对象的比较:
    默认情况下,==比较符比较的是对象的引用是否相等(或者说对象的地址是否相同),我们需要重写魔法方法来改写比较规则
    def __eq__(self, other):  # 判断对象是否相等
        return self.x == other.x and self.y == other.y

    def __gt__(self, other):  # 判断对象大小 greater than
        return self.x > other.x and self.y > other.y


point = Point(2, 3)
other = Point(1, 2)
print(point > other) # True
print(point == other) # False

重写了>(__gt__)之后就不用再重写<了。

  1. 对象的计算
def __add__(self, other):
        return Point(self.x+other.x, self.y+other.y)

34.定制容器

class TagCloud:
    def __init__(self):
        self.__tags = {}  # 前双下划线使得属性私有

    def add(self, tag):
        self.__tags[tag.lower()] = self.__tags.get(tag.lower(), 0)+1

    def __setitem__(self, tag, count):  # 激活[]赋值
        self.__tags[tag.lower()] = count

    def __getitem__(self, tag):  # 激活[]访问
        return self.__tags.get(tag.lower(), 0)

    def __len__(self):  # 激活len()
        return len(self.__tags)

    def __iter__(self):  # 使得iterable,需要返回一个iterator
        return iter(self.__tags)



cloud = TagCloud()
cloud.add('python')
cloud['c#'] = 10
cloud['java'] = 21
print(len(cloud))
for i in cloud:
    print(f"{i}:{cloud[i]}")

值得一提的是,前双下划线并不能使属性完全私有,它更多是一种惯例和规范上的预防和提示。

  1. @property
    又一个非常方便的Python语法特性
class Product:
    def __init__(self, value):
        self.price = value

    @property
    def price(self):
        return self.__price

    @price.setter
    def price(self, value):
        if value < 0:
            raise ValueError
        self.__price = value

我们使用@property修饰方法,此时会把方法直接变成同名属性。当我们获取属性的值时,实际上就是调用的此方法。
此时,本身又自动创建了另一个装饰器,负责把一个方法变成属性赋值,即:price的setter方法。于是,修饰的其实是,当我们给price赋值时,实际上就是调用的此方法。此时,price对外表现得就像一个普通的attribute

  1. 继承
class Animal:
    def __init__(self):
        self.age = 1

    def eat(self):
        print('eat')


class Mammal(Animal):
    def walk(self):
        print('walk')


m = Mammal()
print(m.age) # 1
m.eat() # eat
print(isinstance(m, object))  # True
print(issubclass(Mammal, Animal))  # True

所有的类都直接或者间接继承自object
除此以外,还可以非常方便地通过继承扩展内置类型(比如str,list等)

  1. 继承中的构造器
    不同于Java或者C++的构造器机制(类同名,默认构造器,从父到子),Python中的构造器只有一个名字__init__,这也造成了一个神奇的结果:Python的构造器是可以覆盖的,而且子类的实例化可以完全不执行父类的构造方法。如果需要在子类的构造器中执行父类的构造器,需要显式指明
    def __init__(self):
        super().__init__()
        self.weight = 2

另外,如果子类没有自己的构造器,则只会执行父类的构造器,也就是说不存在所谓的默认构造器

  1. Python中的抽象类和抽象方法
    机制大同小异,不再赘述:
from abc import ABC, abstractmethod


class Stream(ABC):  # 抽象类

    @abstractmethod
    def read(self):  # 抽象方法
        pass


class MemoryStream(Stream):

    def read(self):
        print('read')


stream = MemoryStream()

值得注意的是,Python并不支持函数重载,这也意味着所谓的重写(覆盖)的函数签名不包括形参,而只是函数名

  1. 多态
    由于Python中不存在所谓的上转型对象,所以实现多态的方式也有所变化。
    可以简单的通过一个函数实现
def operate(control):
    control.do_something()

显然,只要对象存在该方法,即使不在同一继承体系也可以成功执行,即“is-a“变成了“like-a"

  1. 纯数据类
from collections import namedtuple

Point = namedtuple("Point", ['x', 'y'])

p1 = Point(x=1, y=2)
p2 = Point(x=1, y=2)
print(p1 == p2)

我们不必在重写魔法方法来定义比较规则,同时这种方法创建的数据类是不可变的

  1. 引入模块的方式
from sales import calc_shipping # 此时calc_shipping就好像在该文件定义一样直接使用
import sales # 此时sales可以看作一个对象,通过sale.doSomething访问模块成员
  1. Python编译
    一旦我在main里import了app并运行,就会产生如图的文件
    [站外图片上传中...(image-14bf3c-1553528837227)]
    实际上,pycache里存放了app的编译结果作为缓存,加快下次引入app模块的速度。
    同时,Python不会编译作为入口的文件(此处是main.py)
  2. 模块路径
    就如同Java的类路径,Python也有模块路径用于在程序import时搜寻相应的模块,当前目录就是其中之一。
import sys
print(sys.path) # 打印所有模块路径
  1. 包(package)
    包简单来说就是模块的集合,对应到文件层面包就是一个目录,模块就是一个py文件
    新建一个目录,并在其中新建一个名为__init__.py的文件,这个目录就变成了一个包,同时包还可以有子包,同样创建一个子目录和init文件,以此类推。
    那么如何import同级子包中的模块呢?
from 最顶级包.目标包 import 目标模块
from ..目标包 import 目标模块 # 两个点表示上级目录(包)

两种方法分别对应绝对和相对寻址

import app

print(dir(app))  #返回scope的所有属性名
print(app.__name__) # 模块名
print(app.__file__)  # 路径
print(app.__package__)

值得注意的是,如果直接执行某个模块,模块名(__name__)总会是__main__,所以也就有了常见的:

if __name__ == "__main__":
    pass
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容