数据结构主要包括:数组(Array)、集合(Set)、列表(List)、队列(Queue)、链表(Linkedlist)、树(Tree)、堆(Heap)、栈(Stack)和字典(Dictionary)等
Python中数据容器主要有:序列、集合和字典
注意:Python中没有数组结构
序列(sequence)
[start:end]
:start
是开始索引,end
是结束索引[start:end:step]
:start
是开始索引,end
是结束索引,step
是步长。步长可以为正整数,也可以为负整数。a = a[::-1]
元组:一种不可变的序列,一旦创建不可以修改
创建元素可以使用tuple([iterable])
函数或者直接用逗号,
将元素分割
访问元组:索引访问元素、分片、拆包(Unpack)
a = ('Hello','World',1,2,3)
str1,str2,*n = a # 拆包
# str1 = 'Hello'
# str2 = 'World'
# n = [1,2,3] # 列表
遍历元组:遍历序列一般都是用for
循环
a = (5,4,3,2,1)
for idx, item in enumerate(a):
print("{0} - {1}".format(idx,item ))
也是一种序列,与元组的区别是:元素无法修改,而列表可修改,包括追加、删除、替换和插入
列表创建
list([iterable])
函数,或者用中括号[]
将元素包裹,元素之间用逗号,
分隔
追加元素
①append()
方法:追加单个元素
②+
运算符或extend()
方法:追加另外一个列表
插入元素
list.insert(i, x)
:i
指要插入的索引,x
是要插入的元素
替换元素
删除元素
①remove(x)
方法,x
是要删除的元素,如果没有x
元素,会抛出异常
②pop()
方法,pop(i)
删除i
索引值,pop()
删除最后面的元素,有 返回值:删除的元素
其他常用方法
①reverse()
:倒置列表
②copy()
:复制列表
③clear()
:清除列表中的所有元素
④index(x[,i[,j]])
:返回查找x第一次出现的索引,i
是开始查找索引,j
是结束查找索引
⑤count(x)
:返回x
出现的次数
列表推导式
n_list = [x ** 2 for x in range(10) if x % 2 == 0]
print(n_list)
# 相当于
n_list = []
for x in range(10):
if x % 2 == 0:
n_list.append(x ** 2)
print(n_list)
# 多个条件时
n_list = [x for x in range(100) if x % 2 == 0 if x % 5 == 0]
n_list = [x for x in range(100) if x % 2 == 0 and x % 5 == 0]
集合的特点:集合中的元素是无序的、不能重复的。包含可变集合和不可变集合
创建可变集合
set([iterable])
函数,或者用大括号{}
修改可变集合
①add(elem)
:添加元素,如果元素已经存在则不能添加,且不会抛出错误
②remove(elem)
:删除元素,如果元素不存在,则抛出错误
③discard(elem)
:删除元素,如果元素不存在,不会抛出错误
④pop(elem)
:删除返回集合中的任意一个元素,返回值是删除的元素
⑤clear()
:清除集合
遍历集合:for in
不可变集合
frozenset([iterable])
函数,不能使用大括号{}
student_set = frozenset(['张三','李四','王五','王五'])
集合推导式
n_set = {x ** 2 for x in range(10) if x % 2 == 0}
print(n_set)
dict
是可以迭代的、可变对象
创建字典
dict()
函数,或者用大括号{}
将“键:值”对包裹,“键:值”对之间用逗号分隔
dict1 = {102: '张三', 105: '李四', 109: '王五'}
print(dict1)
dict2 = dict(S102='张三', S105='李四', S109='王五')
print(dict2)
print(dict1[102])
print(dict2['S102'])
注意:特殊形式创建dict(key1=value1,key2=value2,key3=value3...)
,key
必须是字符串,而且字符串要省略单引号或双引号
修改字典
①添加
②替换
③删除:del
语句、pop(key[,default])
方法和popitem()
方法
dict1 = {102: '张三', 105: '李四', 109: '王五'}
del dict1[109]
dict1.pop(105)
dict1.popitem() # 随机删除
访问字典
①get(key[,default])
:通过键返回值,如果键不存在返回默认值
②items()
:返回字典的所有键值对
③keys()
:返回字典键视图
④values()
:返回字典值视图
遍历字典
dict1 = {102: '张三', 105: '李四', 109: '王五'}
for k in dict1.keys():
print(k)
for v in dict1.values():
print(v)
for id, name in dict1.items():
print(id, name)
字典推导式
input_dict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
output_dict = {k: v for k, v in input_dict.items() if v % 2 == 0}
print(output_dict)
output_dict = [k for k, v in input_dict.items()]
print(output_dict)
内置函数(Built-in Functions
,缩写BIF
):Python官方提供
def 函数名(参数列表):
函数体
return 返回值 # 没有数据返回可省略
def rectangle_area(width, height):
area = width * height
return area
r_area = rectangle_area(320.0, 480.0)
print(r_area)
使用关键字参数调用函数
def print_area(width, height):
area = width * height
print('{0} * {1} 长方形的面积是{2}'.format(width, height, area))
print_area(width=320.0, height=480.0)
参数默认值
def make_coffee(name="卡布奇诺"):
return "制作一杯{0}咖啡".format(name)
coffee1 = make_coffee("拿铁")
coffee2 = make_coffee()
print(coffee1)
print(coffee2)
可变参数
Python中可变参数有两种,在参数前加*
或**
形式,*
可变参数在函数中被组装成为一个元组,**
可变参数在函数中被组装成为一个字典
①*
可变参数
def sum(*numbers, multiple=1):
total = 0.0
for number in numbers:
total += number
return total * multiple
print(sum(100.0, 20.0, 30.0))
print(sum(30.0, 80.0, multiple=2))
d_tuple = (50.0, 60.0, 0.0)
print(sum(30.0, 80.0, *d_tuple))
②**
可变参数
def show_info(sep=":", **info):
print("---------------")
for key, value in info.items():
print("{0} {2} {1}".format(key, value, sep))
show_info("->", name="Tony", age=18, sex=True)
无返回值函数
return None
多返回值函数:返回元组类型数据,元组是不可变的,比较安全
def position(dt, speed):
posx = speed[0] * dt
posy = speed[1] * dt
# return (posx, posy)
return posx, posy
move = position(60.0, (10, -5))
print(move)
变量可以在模块中创建,作用域是整个模块,称为全局变量。
变量也可以在函数中创建,默认情况下作用域是整个函数,称为局部变量。
# 全局变量x
x = 20
def print_value():
global x # 声明调用全局变量
x = 10
在一个函数中使用return
关键字返回数据,但是有时候会使用yield
关键字返回数据。yield
关键字的函数返回的是一个生成器(generator
)对象,生成器对象是一种可迭代对象。通过__next__()
方法获得元素。
# def square(num):
# n_list = []
# for i in range(1, num + 1):
# n_list.append(i * i)
#
# return n_list
def square(num):
for i in range(1, num + 1):
yield i * i
for i in square(5):
print(i, end=' ')
yield
语句可以返回一个元素,此时的函数返回的生成器对象,生成器对象是一种特殊迭代对象。
注意:生成器特别适合用于遍历一些大序列对象,它无须将对象的所有元素都载存入内存后才开始进行操作。而是仅在迭代至某个元素时才会将该元素载放入内存。
函数还可以定义在另外的函数体中,称作“嵌套函数”。
1、嵌套函数可以访问所在外部函数中的变量,但外部函数不能访问嵌套函数局部变量。
2、不能在外部函数体之外直接访问嵌套函数。
def calculate(n1, n2, opr):
multiple = 2
# 定义相加函数
def add(a, b):
return (a + b) * multiple
# 定义相减函数
def sub(a, b):
return (a - b) * multiple
if opr == '+':
return add(n1, n2)
else:
return sub(n1, n2)
print(calculate(10, 5, '+'))
print(calculate(10, 5, '-'))
函数式编程(functional programming)与面向对象编程一样都是一种编程范式,函数式编程,也称为面向函数的编程。
函数式编程所能处理的是一些计算,处理数据
函数类型
所有函数都有函数类型,函数类型实例与普通类型实例,使用的场景是一样的:可以赋值给一个变量;可以作为一个参数使用;也可以作为函数的返回值使用。
def calculate_fun(n1, n2, opr):
multiple = 2
# 定义相加函数
def add(a, b):
return (a + b) * multiple
# 定义相减函数
def sub(a, b):
return (a - b) * multiple
if opr == '+':
return add
else:
return sub
f1 = calculate_fun('+')
f2 = calculate_fun('-')
print(f1(10, 5))
print(f2(10, 5))
Lambda表达式
Lambda表达式本质上是一种匿名函数,匿名函数也是函数,有函数类型,也可以创建函数对象。
定义Lambda表达式语法如下:
lambda 参数列表: Lambda体
def calculate_fun(opr):
multiple = 2
if opr == '+':
return lambda a, b: (a + b) * multiple
else:
return lambda a, b: (a - b)
f1 = calculate_fun('+')
f2 = calculate_fun('-')
print(f1(10, 5))
print(f2(10, 5))
注意:Lambda体部分不能是一个代码块,不能包含多条语句,只有一条语句,语句会计算一个结果返回给Lambda表达式,但是与函数不同的是,不需要使用return
语句返回。与其他语言中的Lambda表达式相比,Python中提供Lambda表达式只能处理一些简单的计算。
三大基础函数
函数编程主要是对数据进行处理:过滤、映射和聚合等。
Python提供了三个基础函数:filter()
、map()
和reduce()
。
1、filter()
:filter(function, iterable)
返回布尔值,在function
函数中编写过滤条件,如果为True
的元素被保留,如果为False
的元素被过滤掉。filter
函数返回一个列表。
users = ['Tony', 'Tom', 'Ben', 'Alex']
users_filter = filter(lambda u: u.startswith('T'), users)
print(list(users_filter))
number_list = range(1, 11)
number_filter = filter(lambda it: it % 2 == 0, number_list)
print(list(number_filter))
2、map()
:map(function, iterable)
映射操作,可以对可迭代对象的元素进行变化。
users = ['Tony', 'Tom', 'Ben', 'Alex']
users_map = map(lambda u: u.lower(), users)
print(list(users_map))
3、reduce()
:reduce(function, iterable)
聚合操作中最基础的是归纳函数reduce()
,会将多个数据按照指定的算法积累叠加起来,最后输出一个数据。
from functools import reduce
a = (1, 2, 3, 4)
a_reduce = reduce(lambda acc, i: acc + i, a) # acc是初始值
print(a_reduce)
面向对象的编程思想:按照真实世界客观事物的自然规律进行分析,客观世界中存在什么样的实体,构建的软件系统就存在什么样的实体。
封装性:封装能够使外部访问者不能随意存取对象的内部数据,隐藏了对象的内部细节,只保留有限的对外接口。外部访问者不用关心对象的内部细节,使得操作对象变得简单。
继承性:特殊类拥有一般类的全部数据和操作,称为特殊类继承一般类。一般类称为“父类”或“超类”,特殊类称为“子类”或“派生类”,为了统一,本书一般类统称为“父类”,特殊类统称为“子类”。
Python、C++支持多继承。
Java、Swift、OC都只支持单继承。
多态性:多态性是指在父类中成员被子类继承之后,可以具有不同的状态或表现行为。
Python中一个类的实现包括:类定义和类体。
class 类名[(父类)]:
类体
class Animal(object):
# 类体
# 类的成员
pass
提示:代码的pass语句什么都不做,用于维持结构的完整。
对象也称为实例。一个对象生命周期:创建对象、使用对象和销毁对象。
销毁对象:Python中程序员不用释放对象内存。
创建对象:通过构造方法,Animal()
使用对象:anim = Animal()
,anim
是指向对象的引用。
anim = Animal()
print(anim)
提示:__str__()
这种双下划线开始和结尾的方法,是Python保留的,有着特殊的含义,称为魔法方法。
提示:在Python类成员中有attribute
(成员变量)和property
(属性)。
attribute
是类中保存数据的变量,如果需要对attribute
进行封装,那么在类的外部为了访问这些attribute
,往往会提供一些setter
和getter
访问器,setter
访问器是对attribute
赋值的方法,getter
访问器是取attribute
值的方法,这些方法在创建和调用都比较麻烦,于是Python又提供了property
,property
本质上就是setter
和getter
访问器,是一种方法。
实例变量:某个个体实例的变量。
class Animal(object):
def __init__(self, age, sex, weight):
self.age = age
self.sex = sex
self.weight = weight
anim = Animal(2, 1, 10.0)
print(anim.age, anim.sex, anim.weight)
类变量是所有实例(或对象)共有的变量。
class Account:
# 利率 创建类变量
interest_rate = 0.0668
def __init__(self, owner, amount):
self.owner = owner
self.amount = amount
account = Account('Tony', 10000)
print(account.owner, account.amount)
print(Account.interest_rate)
注意:不要通过实例存取类变量数据。当通过实例读取变量时,首先Python解释器会先在实例中找这个变量,如果没有再到类中去找;当通过实例为变量赋值时,无论类中是否有同名变量,Python解释器会创建一个同名实例变量。
__init__()
构造方法,用来初始化类中实例变量。
实例方法与实例变量类似,属于类的某个个体的方法。
第一个参数self
,当前实例与该方法绑定了,所以该方法就是实例方法。
class Animal(object):
def __init__(self, age, sex=1, weight=8.0):
self.age = age
self.sex = sex
self.weight = weight
def eat(self):
self.weight += 0.05
print('eat...')
def run(self):
self.weight -= 0.01
print('run...')
anim = Animal(2, 1, 10.0)
print(anim.__dict__)
anim.eat()
print(anim.__dict__)
anim.run()
print(anim.__dict__)
类方法与类变量是类似的,都属于类,不属于类的个体。类方法不需要与实例进行绑定,第一个参数不是self
,但是它需要类绑定,第一个参数是type
类型,type
是描述数据类型的类。
@classmethod
,@
表示装饰器,是Python 3提供的,修饰方法、函数、类,起到约束的作用。
class Account:
# 利率 创建类变量
interest_rate = 0.0668
# 类方法
@classmethod
def interest_by(cls, amt):
print(type(cls))
return cls.interest_rate * amt
ints = Account.interest_by(12_000)
print(ints)
注意:类方法中可以访问其他的类变量和类方法,但是不能访问实例变量和实例方法。
类中定义方法,既不想与类绑定,也不想与实例绑定,这种方法就是静态方法。
总结:静态方法与类方法在很多场景是类似的,只是在定义时有一些区别,类方法需要绑定类,静态方法不需要绑定类,静态方法与类的耦合度更加松散。在一个类中定义静态方法,只是为了提供一个基于类名的命名空间。
class Account:
# 利率 创建类变量
interest_rate = 0.0668
# 类方法
@classmethod
def interest_by(cls, amt):
print(type(cls))
return cls.interest_rate * amt
# 静态方法
@staticmethod
def interest_with(amt):
# return Account.interest_rate * amt
return Account.interest_by(amt) # 可调用类方法
ints = Account.interest_with(12_000)
print(ints)
默认声明的变量是公用的,私有变量就是在变量前加双下划线(__
)。
提示:Python中并没有严格意义上的封装,所谓的私有变量只是形式上的限制,如果想在类的外部访问这些私有变量也是可以的,这些双下划线(__
)开头的私有变量,其实只是换了一个名字,他们的命名规律为“_类名__变量
”,所以如a1.weight
改成a1._Animal__weight
就可以访问了,但这种访问方式并不符合规范,会破坏封装。
私有方法封装与私有变量是类似的,就是在方法前加双下划线(__
)。
面对对象设计:一个类是不应该有公有的实例成员变量的,这些实例成员变量应该被设计为私有的,然后通过公有的setter
和getter
访问器访问。
class Animal(object):
def __init__(self, age, sex=1, weight=0.0):
self.age = age
self.sex = sex
self.__weight = weight
def set_weight(self, weight):
self.__weight = weight
def get_weight(self):
return self.__weight
anim = Animal(2, 1, 10.0)
anim.set_weight(10.3)
print(anim.get_weight())
这可以通过定义属性(property
)实现。定于属性可以使用@property
和@属性名.setter装饰器
,@property
用来修饰getter
访问器,@属性名.setter
用来修饰setter
访问器。
class Animal(object):
def __init__(self, age, sex=1, weight=0.0):
self.age = age
self.sex = sex
self.__weight = weight
# 须先定义getter访问器
@property
def weight(self):
return self.__weight
@weight.setter
def weight(self, weight):
self.__weight = weight
anim = Animal(2, 1, 10.0)
anim.weight = 10.3
print(anim.weight)
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
template = 'Person [name={0}, age={1}]'
s = template.format(self.name, self.age)
return s
# class Student:
# def __init__(self, name, age, school):
# self.name = name
# self.age = age
# self.school = school
#
# def info(self):
# template = 'Person [name={0}, age={1}, school={2}]'
# s = template.format(self.name, self.age, self.school)
# return s
class Student(Person):
def __init__(self, name, age, school):
super().__init__(name, age)
self.school = school
stu = Student('Tony', 20, '清华大学')
print(stu.info())
子类中有与父类具体相同的方法名,而且方法的参数也相同。子类就会重写(Override
)父类方法。
class Student(Person):
def __init__(self, name, age, school):
super().__init__(name, age)
self.school = school
def info(self):
template = 'Person [name={0}, age={1}, school={2}]'
s = template.format(self.name, self.age, self.school)
return s
stu = Student('Tony', 20, '清华大学')
print(stu.info())
class Animal(object):
"""定义动物类"""
def __init__(self, age, sex=1, weight=0.0):
self.age = age
self.sex = sex
self.weight = weight
def eat(self):
self.weight += 0.05
print('动物吃...')
class Dog(Animal):
def eat(self):
self.weight += 0.1
print('狗狗吃...')
a1 = Dog(2, 0, 10.0)
a1.eat()
多继承可能会发生命名冲突。
Python支持多继承,但Python给出了解决名字冲突的方案。这个方案是当子类实例调用一个方法时,先从子类中查找,如果没有找到则查找父类。父类的查找顺序是按照子类声明的父类列表从左到右查找,如果没有找到再找父类的父类,依次查找下去。