python学习——基本数据类型操作和函数

系列解包赋值

a,b=1,2
#a=1
#b=2
a,b,c=1,2,3
#a=1,b=2,c=3

实现四舍五入

a=1.6
print(int(a+0.5))
print(round(a))

整数的缓存问题

python会将[-5,256]内的整数对象进行缓存,也就是说这个范围内的对象只会指向一个内存块

在pycharm里 会做一些优化把范围扩展到[-5,无穷]

a=10
b=10
print(id(a))
print(id(b))
a=-6
b=-5
print(id(a))
print(id(b))

'''
输出
140713838921824
140713838921824
2049070469808
140713838921344
'''

字符串

不支持通过索引的方式修改。

但是可以通过replace方法修改

replace(要替换的字符,要替换成的字符)

原理就是replace方法相当于重新创建了一个字符串,然后完成替换,并使原标识符引用这个新的字符串

切片

[起始点:终止点:步长]

举个例子 a='0123456'

则有 a[:]='0123456'

a[3:]='3456'

a[-2:]='56'

a[::2]='0246'

a[::-1]='6543210'

split将字符串按照分隔符分割,并将其存入列表

split(sep)sep为分隔符,默认为换行 空格 制表符 

a='a b c d e f'
b=a.split()
print(b)
'''
输出
['a', 'b', 'c', 'd', 'e', 'f']

'''

join方法可以将列表里的字符组合起来

'分隔符'.join(要合并的列表)

a='a b c d e f'
b=a.split()
print(b)
c=''.join(b)
print(c)


'''
输出
['a', 'b', 'c', 'd', 'e', 'f']
abcdef
'''

+也可以连接字符串 但是如果要连接多个字符串,join效率更高

import time
time1= time.time()
s=''
for i in range(1000000):
    s+='123'
time2=time.time()
print(time2-time1)


time1= time.time()
s=[]
for i in range(1000000):
    s.append('123')
''.join(s)
time2=time.time()
print(time2-time1)

测试将'123'连接1000000次所需时间

+需要0.6163523197174072s

join需要0.11173439025878906s

显然是join更胜一筹

字符串驻留机制

仅包含符合标识符规则字符的标识符(仅包含下划线 字母 数字)python对于相同的字符串仅创建一个,若含有其他字符 如'#'就会创建多个

常用查找方法

len()查看字符串长度

startswith()是否以指定字符串开头

endswith()是否以指定字符串结尾

find()返回指定字符第一次出现的位置

rfind()返回指定字符最后一次出现的位置

count()返回指定字符串出现了几次

strip()去除首尾指定信息,默认是换行 空格

lstrip()去除左边指定信息,默认是换行 空格

rstrip()去除右边指定信息, 默认是换行 空格

title()产生新的字符串,每个单词首字母大写

upper()产生新的字符串,所有字符转大写

lower()产生新的字符串,所有字符转小写

swapcase()产生新的字符串,所有字母大小写转换

格式排版

a='SXT'

a.center(10,'*')

"***SXT****"

a.center(10)

"   SXT    "

a.ljust(10,'*')

"SXT*******"

a.rjust(10,'*')

"*******SXT"

可变字符串

包含模块 io

基本语法如下

import io
s = '123456'
sio = io.StringIO(s)
print(sio.getvalue())#输出字符串
sio.seek(5)#移动到索引为5的位置
sio.write('7')#用7替换索引为5的字符
print(sio.getvalue())

'''
123456
123457

'''

列表

语法:

变量名 = [a,b,c,d] a,b,c,d可代表 int double string char 等数据类型

列表的创建

1.基本语法创建

a=[1,2,3,4]

2.list方法创建

a=list(range(1,10))

注意list()可以接受元组 字符串 其他序列类型

3.推导式生成列表

a=[x*2 for x in range(5)]

a=[0,2,4,8]

a=[x*2 for x in range(100) if x%9==0]

a=[0,18,36,54,72,90,108,126,144,162,180,198]

访问列表:

利用下标方式访问,类似于数组,但是可以用负数下表,-1代表倒数第一个数据

修改某个元素:

直接通过下标,来修改值。

在列表尾部插入元素:在列表尾加元素,利用append()方法

例如 a=[1,2,3]

a.append(4)

就是把4插入到列表尾部

将两个列表相加 得到一个合并的列表,这个过程中创建了新的列表对象,不推荐这样使用。

可以使用extend()在原有对象基础上拓展。

在列表中插入元素:

利用insert(x(下标),数据)方法。将数据插入到x后

从列表中删除元素:

del a[x] 删除a列表中下标尾x的元素

del a 删除a列表

pop()方法,取走列表尾部元素

在括号里加上索引可以取走任意位置的元素如 pop(0)取走索引为0的元素

利用remove()方法删除给定值的元素,如a.remove(1)删除a列表里1

组织列表:

sort()方法对列表进行永久性排序

可以按字母顺序相反顺序排列 只需要使用 sort(revese =True)

sorted()对列表进行临时排序,即不会改变列表顺序,可以用在print里

reserve()反转列表

len()方法求列表长度

遍历整个列表

chars=[1,2,3,4]

for a in chars:

print(a)

含义是从chars中取元素赋给a,并输出a

创建数值列表

使用range()函数

for value in range(1,5)

    print(value)

输出从1到4.

使用range()创建数字列表

numbers = list(range(1,6))

则numbers = [1,2,3,4,5]

range还可以指定步长

如for a in range(1,10,2)

  print(a)

则将输出 2 4 6 8

min max sum 函数均可运用在列表里,使用时候是 min(列表名)

列表解析

列表解析可以让我们一行代码就可以创建一个特定的列表

如 a = [value**2 for value in range(1,11)]

于是 a=[1,4,9,16,25,36,49,64,81,100]

筛选大于0的元素

b=[i for  i in a if i>0]

使用列表的一部分

切片

通过切片我们可以输出第几个到第几个之间的元素

如 a=[1,2,3,4]

print(a[0,2])

输出[1,2,3]

若不指定第起始索引则从表头开始,不指定终止索引则会一直输出到表尾

如果你想输出最后2个元素

可以这样 print(a[-2:])

切片也可以遍历

例如 for i in a[-2;]

           print(i)

复制列表

a=[1,2,3,4,5]

b=a

这样是行不通的,只是b指向了a列表。

正确的方法应该是使用切片

b=a[:]把从头到尾的a的元素赋给b

多维列表

a = [[1,2,3],[4,5,6],[7,8,9]]
a[0][0] = 1

元组

元组跟列表类似也可以存储任意基本数据类型

但是元组用()包含元素,而且一旦初始化,元素不能被修改。但是可以赋值

创建元组

1.语法创建

a = (1,2,3)
b = 1,2,3
c = (1,)#如果不加逗号程序会默认这个1为一个数字

2.通过tuple()函数创建

注意tuple()可以接受列表 字符串 其他序列类型

zip

zip(列表1,列表2,......)将多个列表对应位置元素组合成元组,并返回这个zip对象

a = [1,2,3]
b = [4,5,6]
c = [7,8,9]
d=zip(a,b,c)
print(d)
#输出
<zip object at 0x000001F0120C2D40>

d=list(zip(a,b,c))
print(d)
#输出
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

生成器推导式创建元组

与列表推导式雷系,只是生成器推导式使用小括号,列表推导式直接生成列表对象,生成器推导式生成的不是元组也不是列表,而是一个生成器对象,我们可以使用tuple()函数将其转换为元组

s = (x*2 for x in range(5))
print(s)
#输出
#<generator object <genexpr> at 0x000001E3EA7DFE40>
a = tuple(s)
print(a)
#输出
#(0, 2, 4, 6, 8)
b = tuple(s)
print(b)
#输出
#()

为什么会生成()呢?

是因为s是生成器对象,生成器对象是通过迭代的方式遍历,所以当使用过一次tuple()也就是说明已经遍历过一次了,这是迭代器会指向最后一个元素,所以如果再调用一次tuple()会生成一个空元组

这说明 需要重新访问生成器对象中的元素,都需要重新创建该生成器对象

元组总结

1.元组的核心特点是:不可变序列

2.元组的访问和处理速度比列表快

3.与整数和字符串一样,元组可以作为字典的键,列表则不可以,但是都可以作为值

集合

使用set()函数创建集合

add(x) 方法将x添加到集合里

remove()方法删除指定元素

clear()方法清空集合

设a,b为两个集合

a-b为差集

a&b为交集

a|b为并集

if语句

python里的if条件不需要用圆括号,而且不需要用{},只需要加冒号,没有else if 。

如, if a==b:

        elif(a>b):

        else:

使用 and 来替代&& or来替代||

判断列表a是否为空

if a:

判断b中元素是否在a中

a=[1,2,3,4]

b=[1,2]

a=[1,2,3,4]
b=[1,2]
for i in b:
  if i in a:
    print("yes")
  else:
    print("no")

字典

字典是一系列键-值对,如 a={'a':1,'b':2}

创建字典

1.基本语法

a = {'a':1,'b':2}
b = dict(a='1',b='2')
c = dict([('a',1),('b',2)])
d = {}
print(a,b,c,d,sep='\n')
'''
输出
{'a': 1, 'b': 2}
{'a': '1', 'b': '2'}
{'a': 1, 'b': 2}
{}
'''

2.通过zip创建字典

k = {'a','b','c'}
v = {1,2,3}
d = dict(zip(k,v))
print(d)
'''
输出
k = {'a','b','c'}
v = {1,2,3}
d = dict(zip(k,v))
print(d)
'''

3.通过fromkeys创建值为空的字典

a = dict.fromkeys(['name','age','job'])
print(a)
'''
输出
{'name': None, 'age': None, 'job': None}
'''

访问字典中的值

如 print(a['a'])即可输出1

向空字典中添加值

a={}
a[1]='a'
a[2]='b'
则a=[1:'a',2:'b']

get()方法获得值,优点是指定键不存在的时候返回None,也可以给其定义默认返回值

a = {'a':'','b':2}
print(a.get('dwa'))
print(a.get('dwa','不存在该键'))
'''
输出
None
不存在该键
'''

修改字典中的值

通过键索引可以直接修改

删除键值对

使用del+ 字典名[键]来删除对应的键值对

遍历字典中的键值对

b={'a':1,'b':2,'c':3}
for key in b:
    print(key)

是输出b里的键

b={'a':1,'b':2,'c':3}
for key in b.items():
    print(key)

输出为

('a', 1)
('b', 2)
('c', 3)

输出每个元素

b={'a':1,'b':2,'c':3}
for key,value in b.items():
    print(key,value)

输出结果为
a 1
b 2
c 3

遍历字典中所有的键

b={'a':1,'b':2,'c':3}
for key in b.keys():
    print(key)


输出结果为
a
b
c

遍历字典中的所有的值

b={'a':1,'b':2,'c':3}
for key in b.values():
    print(key)

可以使用set来提取字典中不一样的项

b={'a':1,'b':2,'c':3,'d':3}
for key in set(b.values()):
    print(key)

update()将新字典中所有键值对全部添加到旧字典对象,如果key重复,直接覆盖

a = {'a':'','b':2}
b = {'a':1,'c':3}
b.update(a)
print(b)
'''
输出
{'a': '', 'c': 3, 'b': 2}
'''

可以看出a中的'a':''将b中的'a':1覆盖了,就是说只要键相同就覆盖掉键值对

列表里套字典

a={'a':1,'b':2,'c':3}
b={'d':4,'e':5}
c=[a,b]

序列解包用于字典

b = {'a':1,'b':2,'c':3}
x,y,z = b
print(x,y,z)
x,y,z = b.values()
print(x,y,z)
x,y,z = b.items()
print(x,y,z)
'''
输出
a b c
1 2 3
('a', 1) ('b', 2) ('c', 3)
'''

字典存储列表

a={'a':[1,2,3],'b':[4,5,6]}

字典里存字典

a={'a':{'b':1},'b':{'c':2}}

字典的核心底层原理(重要)

字典对象的核心是散列表。散列表是一个稀疏数组(总是有空白元素的数组),数组的每个单元叫做 bucket。每个 bucket 有两部分:一个是键对象的引用,一个是值对象的引用。所有 bucket 结构和大小一致,我们可以通过偏移量来读取指定 bucket。下面通过存储与获取数据的过程介绍字典的底层原理。

存储数据的过程

例如,我们将‘name’ = ‘张三’ 这个键值对存储到字典map中,假设数组长度为8,可以用3位二进制表示。


>>> map = {}

>>> map

{}

>>> map['name'] = '张三'

>>> bin(hash('name'))

'0b101011100000110111101000101010100010011010110010100101001000110'

2、用散列值的最右边 3 位数字作为偏移量,即“110”,十进制是数字 6。我们查看偏移量 6,对应的 bucket 是否为空。如果为空,则将键值对放进去。如果不为空,则依次取右移 3 位作为偏移量,即“000”,十进制是数字0,循环此过程,直到找到为空的 bucket 将键值对放进去。python 会根据散列表的拥挤程度扩容。“扩容”指的是:创造更大的数组,将原有内容拷贝到新数组中。接近 2/3 时,数组就会扩容。扩容后,偏移量的数字个数增加,如数组长度扩容到16时,可以用最右边4位数字作为偏移量。

获取数据的过程

1、计算name的散列值

2、用最右边 3 位数字作为偏移量,即“110”,十进制是数字6。查看偏移量 6,对应的 bucket 是否为空。如果为空,则返回 None。如果不为空,则将这个 bucket 的键对象计算对应散列值,和我们的散列值进行比较,如果相等,则将对应“值对象”返回;如果不相等,则再依次取其他几位数字,重新计算偏移量。循环此过程。

总结:

1.键必须可散列,如数字、元组、字符串;自定义对象需要满足支持hash、支持通过__eq__()方法检测相等性、若 a==b 为真,则 hash(a)==hash(b)也为真。


总结

字典在内存中开销巨大,典型的空间换时间;
键查询速度很快;
往字典里面添加新建可能导致扩容,导致散列表中键的次序变化。因此,不要在遍历字典的同时进行字典的修改。

input函数

message=input()
print(message)

键盘输入,并将其输出,注意input会将输入的数据转化为字符串

如果输入的是数字,而且想得到数字,我们可以使用eval去除掉一对‘’来实现这个操作,也可以直接用int强制类型转换

message=input()
print(message)
print(int(message))
print(eval(message))
输入
123
输出
123
123
123

也可以将一部分输出的内容放在input里,使程序在你输入数据前,打印这部分内容

message=input("hello")
print(message)

while循环

举个简单的例子,打印3次hello world

count=1
while(count<=3):
    print("hello world")
    count+=1

注意 这里不能像C一样用++符号,因为python里没有自增自减运算符,python里的--是负负得正的意思,就是说假如a=1,--a,a还是等于1,在在这个过程中,a首先变成-1,然后又变成1

让用户决定什么时候退出

举个例子 当输入等于123的时候推出

message=''
while message !='123':
    message=input()
    print(message)

删除列表中的特定值

可以使用remove方法来删除,但是如果列表中出现多次,就需要用到循环了。

a=[1,1,2,2,1,3,6]
while 1 in a:
    a.remove(1)
print(a)

函数

定义函数

def fun()://def关键字 定义一个函数,括号必不可少,括号后加冒号
    print("hello")
fun()//执行函数

函数的传递

python中参数的传递都是引用传递

具体操作分为两类

1.可变对象,对可变对象的进行写操作,会直接作用于原对象本身

可变对象具体有:字典、列表、集合和自定义的对象

2.不可变对象进行写操作,会产生一个新的对象空间,并用新的值填充这块空间(起到按值传递的作用,但却不是值传递)

不可变对象有:字符串,数字,元组,function等

传不可变对象为参数的例子如下

def fun (n):
    print(id(n))
    n+=1000
    print(id(n))
fun(10)
'''
输出结果
140706450131040
2596441953104
'''

很明显我们发现两个n的地址不一样了

浅拷贝和深拷贝

浅拷贝就是仅仅创建了一个拷贝了对象的引用。

深拷贝就是连子对象的内存一起全部拷贝一份,对子对象的修改不会改变原对象

浅拷贝内置函数copy

深拷贝内置函数deepcopy

import copy
a = [1,2,[5,6]]
print("浅拷贝======")
b = copy.copy(a)
print("a:",a)
print("b:",b)
b.append(30)
b[2].append(7)#将7加入到第列表中第二个元素里,也就是[5,6]这个列表里
print("a:",a)
print("b:",b)
'''
输出结果为
浅拷贝======
a: [1, 2, [5, 6]]
b: [1, 2, [5, 6]]
a: [1, 2, [5, 6, 7]]
b: [1, 2, [5, 6, 7], 30]
'''

我们可以看到,我们在改变b中那些属于a的元素的时候,a也会跟着改变,说明b引用了a的元素。

用C的角度来说,就相当于列表a其实就是一个指针数组,b指向了一个新的一个指针数组,然后把a的指针数组的值赋给b,b对指针数组里的指针指向的地方进行操作,就会影响到a,但是如果b对指针数组进行操作就不会影响到a

如果是深层拷贝

import copy
a = [1,2,[5,6]]
print("深拷贝======")
b = copy.deepcopy(a)
print("a:",a)
print("b:",b)
b.append(30)
b[2].append(7)
print("a:",a)
print("b:",b)
'''
深拷贝======
a: [1, 2, [5, 6]]
b: [1, 2, [5, 6]]
a: [1, 2, [5, 6]]
b: [1, 2, [5, 6, 7], 30]
'''

这时候b与a再内存中的指向就变成这样了

用C来解释就是 b中指向了一个新的指针数组 ,其中的指针指向的地方跟a不同,但是指针指向地方存的值是相同的,所以修改b指向的这个指针数组里指针指向的位置也不会改变a。

传递不可变对象的时候用的是浅拷贝

传递一个元组时,当你试图修改元组中的元素时,他会报错

a = (1,2,[5,6])
def fun(m):
    m[1]=2
fun(a)

'''
Traceback (most recent call last):
  File "C:/Users/dell/PycharmProjects/untitled/main.py", line 4, in <module>
    fun(a)
  File "C:/Users/dell/PycharmProjects/untitled/main.py", line 3, in fun
    m[1]=2
TypeError: 'tuple' object does not support item assignment
'''

但是可以修改[5,6],比如说在其中添加一个元素

a = (1,2,[5,6])
def fun(m):
    print(id(m))
    m[2].append(3)
    print(id(m))
fun(a)
print(a)

'''
3013210054784
3013210054784
(1, 2, [5, 6, 3])
'''

可以发现,外面的a也跟着变了,不可变对象就是说不能通过引用去修改它的值,但是a[2]是引用了[5,6]这个列表的首地址,不可变对象只规定了不能修改它引用的值,但是没说不可以修改它引用的值  引用的东西(前提是这个东西不是元组,因为元组不可以修改),就是说你可以修改这个列表里的元素。

位置实参和关键字实参

顺序实参就是形参和实参要一一对应

关键字实参是调用函数时在括号里让对应的形参等于对应的实参

def fun(a,b):
    print(a,b)
fun(b=1,a=2)

默认值

默认值就是给函数的形参提供了默认值,这样如果没有给函数提供形参,它会使用默认值来执行函数操作

def fun(a,b=0):
    print(a,b)
fun(a=1)

使用默认值的时候,要先在函数参数表里列出无默认参数的形参,在列出有默认参数的形参,只有这样python才能正确的处理形参和实参

函数返回值

def fun(a,b=0):
    print(a,b)
    return 'ok'
f=fun(a=1)
print(f)

函数返回值不需要指明返回类型

可以将列表 字典传给函数,函数对其进行的修改是永久性的。

如果不想函数修改列表本身,可以创建一个副本过去,只需要将参数改为 列表名[:]即创建一个一模一样的列表传过去

函数可以接收任意数量的参数

def fun(*a):
    print(a)
fun("hello")
fun("hello","world","wodetian")

输出结果为

('hello',)
('hello', 'world', 'wodetian')

这说明函数建立了一个元组来存储这些参数,于是可以用循环来打印这些参数

def fun(*a):
    for i in a:
        print(i)
fun("hello")
fun("hello","world","wodetian")


输出为
hello
hello
world
wodetian

结合位置参数使用任意数量实参

def fun(b,*a):
    print(b)
    for i in a:
        print(i)
fun("hello")

函数会将第一个参数存在b里,剩余的存在a元组中

函数传递键值对

def fun(**x):
    p={}
    for k,v in x.items():
        p[k]=v
    return p
x=fun(a='1',b='2',c='3')
print(x)

输出为
{'a': '1', 'b': '2', 'c': '3'}

这两种参数即*x和**x被称为可变参数

强制命名参数

如果想在可变参数后可以添加参数,前提是参数是命名参数

def fun(*x,a):#直接这样写会报错
    print(x)
x=fun(1,2,3,4)
print(x)

正确做法应该是这样

def fun(*x,a):
    print(x)
fun(1,2,3,a=4)

将函数存储在模块里

 将函数放在一个单独的.py文件里制作成模块,在其他py文件中可以通过import 模块名来载入模块,于是就可以使用该模块里的函数了,具体的原理就是,载入模块后,相当于程序在幕后将模块里的函数全部复制到使用import 的程序中。

#dwa.py
def fun(x):
    return x
#make_fun.py
import dwa
x=dwa.fun(1)
print(x)

注意使用某个模块的函数时候,要在函数前注明所属模块并加上.作为分隔

还可以导入特定的函数,而不是将模块整体导入

from dwa import fun
x=fun(1)
print(x)

这样可以直接使用特定函数,不需要加.和模块名了。

可以使用逗号来一次性导入同一个模块的多个函数

如 form dwa import fun1,fun2,fun3

可以给导入的函数起别名

form dwa import fun as p

可以给导入的模块起别名

import dwa as makefun

可以导入模块中的所有函数(不需要使用.和模块名)

import dwa *

一般不要使用这种表示方法,可能会出现函数重名现象。

关于局部变量和全局变量效率的测试

局部变量的查询和访问速度比全局变量快,循环次数多的地方,通过将全局变量转换为局部变量可以提高运行速度

如我们在进行10^7次开方操作后,可以看到使用全局变量和局部变量的差距

import time
import math
def test1():
    start = time.time()
    for i in range(10000000):
        math.sqrt(30)
    end=time.time()
    print("test1耗时为{0}".format(end-start))
def test2():
    b = math.sqrt
    start = time.time()
    for i in range(10000000):
        b(30)
    end=time.time()
    print("test2耗时为{0}".format(end-start))
test1()
test2()

'''
输出为
test1耗时为1.5555238723754883
test2耗时为1.099611520767212
'''

内部函数

在函数中定义一个函数,并使用它,这样做的话,在函数外无法使用在函数内定义的那个函数

举个例子

def fun():
    def funny():
        print('1')
    funny()
funny()#这个会报错
fun()

具体的作用如下

1.数据隐藏

2.嵌套函数,避免在函数内部使用重复代码

举个例子,输出中文名字和英文名字的函数如下

def printChineseName(familyname,name):
    print("{0}  {1}".format(familyname,name))
def printEnglishName(familyname,name):
    print("{0}  {1}".format(name,familyname))

如何将他俩和一块呢,可以使用嵌套函数

def printName(isChineseName,familyname,name)
    def flag()
        print("{0}  {1}".format(name, familyname))
    if isChineseName:
        flag(familyname,name)
    else:
        flag(name,familyname)

这样可以合在一起了,当然不用嵌套函数也可以实现,但是这样在函数里面,更整洁一点,因为这个flag可能只是printName使用,在外面和其他函数在一起,容易混淆。

nonlocal和global关键字

global声明是全局变量

nonlocal声明是来自外层的变量(来自外层函数)

a = 1
def fun():
    b = 2;
    def happy():
        nonlocal b//必须要声明才能进行修改
        print(b)
        b =3
        global a
        a = 2
    return b

3.闭包

LEGB规则

python在查找“名称”时,是按照LEGB规则查找的

Local->Enclosed->Global->Built in

Local 是函数或类的方法内部

Enclosed 指的是嵌套函数

Global 全局变量

Built in python自己的特殊名称

举个例子

#1.str = '123'
def fun():
    #2.str = '456'
    def funny():
        #3.str = '789'
        print(str)

如果1 2 3 均不注释 则输出789

如果将3注释 则输出456

如果将2也注释掉 则输出123

如果将1也注释掉 则输出<class str> 即python自带关键字

发布了37 篇原创文章 · 获赞 3 · 访问量 2375

猜你喜欢

转载自blog.csdn.net/Stillboring/article/details/105255067