python笔记的汇总

Python 参考笔记

第一部分基础知识

数据类型

基本的五大数据类型

对python中的变量有如下的五种基本的数据类型:

  1. Number数字

  2. list列表

  3. Tuple元组

  4. string字符串

  5. Dictionary字典

Number

[注意:在python中一切的命名是按照贴标签的形式进行初始化的例如下面的:]

a=10.2 #浮点数,其中10.2才是一个对象,而a就是这个对象的名字

str1="Hello world" #创建一个字符对象

s=int(10) #创建一个int对象为了防止python自动类型推导为float
'''

如果要对一个对象的类型进行输出判断的话可以使用type()函数参数就是变量的名字例如:

'''

p=10.2222

print(type(p))

#output: float

Number为数字类型的,数字类型又分为五种基本的数字类型分别为:

int类型

整型数据例如 1 2 3 4....... -1,-2,-3,-4....

在32位机器上,整数的位数为32位,取值范围为-2*** 31~2***31-1,即-2147483648~2147483647

在64位系统上,整数的位数为64位,取值范围为-2*** 63~2***63-1,即-9223372036854775808~9223372036854775807

long

计算机硬件限制,可能出现int无法表示的很大的整型数据,类似于c语言的long,python3是没有python2的long类型的如果你输入下面的:


print(type(Long(123333)))

一定会出现未定义类型Long的错误,这是因为python3中去除了Long类型,但是python3中的int是不限制长度的,你可以把int当作long进行使用

float

float(浮点型数据),也就是带着一切小数点的正数和负数

例如下面的代码


a=1.23 #浮点数1.23

s=2.36 #浮点数2.36

也就是说创建了小数点出现的Number就是浮点数

complex复数

就是常见的复数 分为实部和虚部:a+bi的形式,但是在python中复数的i被替换为j


#创建复数的形式如下:

complex1=1+2j

print(type(complex1))

#还定义了复数的四则运算

#运算符分别为 普通的四则运算符号

complex1*complex1 # *号可以换成 + - /



List

list是一个对象而不是一个数据类型 也就是说 list对象有许多的方法:创建list L=[]即可创建一个空的list,常用操作如下


L=[]

L.append(10) #添加一个数字

L.pop() #默认弹出最后一个数字 可以填数组的指标选择弹出什么元素

L.pop(0) #弹出第一个元素

sum(L) #求数组的所有数字的和

max(L) #求数组中最大值

L.index(num) #num为数组中某个数字 index返回下标

#数组访问采用[]

#直接访问

L[0] #访问 指标为0的数字

#数组切片

L[start:end] #返回从数组的指标start开始一直到end-1开始访问(也就是一个半开半闭的区间访问[start,end))的所有数字

L=[1,2,3,4]

print(L[1:3])

#output:2 3 返回下标为1 2的数字



tuple

tupel和list的大多数功能都是一致的 比如按照[]进行访问,可以切片,不同的地方如下:


tu=() #创建一个空的tuple

#和list不同 tuple在创建的时候就必须要指定值

tu=(1,2,3,4,1,1)

#方法 count()

#计算某个数字在tuple中出现的次数

tu.count(1) #1在tu中出现了3次

#output:3

tu.index(4) #4在tu中的指标,当有多个相同值 返回第一个的时候,比如查找的是1,那么返回的就是0,不管后面有多少个1

#output:3

string

字符串不同于字符 代表一切的字符集合 也可以理解为一个字符list




str1="hello world" #创建一个字符串,可以使用 " "也可以使用 ' '

#你可以把字符串 "hello "看成一个list ['h','e','l','l','o']然后使用切片直接对字符串使用就可以



len(str1) #返回字符串的长度

str1[::-1] #反转字符串

'''字符串函数太多了举几个常用的'''

str1.replace('old','new',max_) #这个函数是这样使用的 old是需要替换的字符串,new是新的替换的字符串,max_参数选填 替换最大的个数是多少,例如:



str2="hello world2"

str2.replace('l','s',2) #意思将 l字符替换为s,最大为2个

#output:hesso world2



str2.upper() #字符串的小写转大写

str2.lower() #转小写

str2.capitalize() #把字符串中的第一个字符转大写

字典型数据

字典型数据分为两个部分构成:(Key,value),利用键值对的形式进行创建,例如有一个字典是Dic 那么访问的方式是 Dic['key'] 会访问到和'key'对应的value,key和value都可以是任何的数据类型


Dic={

 "Name":"zhenzhen",

 "year":19,

 "color":"white"

}



#你可以通过 Dic["Name"]访问到"zhenzhen"这个字符串,也可以通过Dic["year"]访问到19这个数字,以此类推 ':'左边的是key,右边的是value



#如果你不想通过key的方式,要一口气访问所有的Value,那么可以

print(Dic.values())

#打印所有的value



## 逻辑分支和循环

Boolean变量

开始分支和逻辑循环的时候不得不提的是boolean变量(布尔),boolean变量的值只有两种取值:0 和 1,0代表假 1代表真,在python中无需声明一个布尔变量,如果是在函数中 return 1或者 return 0的时候 不加说明的话,比如下面的情况:

def fun(num):
    if num>0 :
       return 0
    else:
       return 1
#函数只有两个返回值 0 和 1,在下面的 不加说明的话 0 和1 会被当作int类型
#但是在下面的情况:
if fun(1):
   '''
   ..code.....
   '''

此时在if条件中 fun(num)的返回值会被当作boolean变量进行对待

逻辑分支语句和循环

逻辑分支语句

if条件语句是如下的格式:

 if (True):
    '''
		code
    '''
 else:
    '''
    code
		'''

如果上面的括号中的表达式 函数返回值 逻辑判断(大于 小于 等于 或者组合判断) 为真的时候就执行下面的code,如果为Fasle的时候就执行else中的code,如果要写成多分支的语句的时候就采用 if+else的情形,如下所示:

if (condition 1):

elif (condition 2):

elif (condition 3):


要是写成多分支的时候就采用上面的形式进行判断,多分支的情况会逐个扫描每一个 condition 如果这个condition为真的话那就执行这个代码块下的内容,其余代码块的条件都不会发生执行

逻辑连词是用来连接两个逻辑条件之间的关系,如果这两个逻辑条件为真或者为假会让整个逻辑条件的真假发生改变,从而可以设置复杂的逻辑判断条件,也就是if 或者 elif 的condition 部分

  1. and

  2. or

  3. in

  4. not in

  5. 数字关系运算符

    5.1 >

    5.2 ==

    5.3 <

#and 是字面意思且的意思,也就是说如果有多个连接的逻辑表达式比如

a=10
if a>0 and a<100:
    print("OK")

在上面的语句中 a>0 和 a<100都是为真的那么整个表达是(a>0 and a<100)都是为真的 也就是 and连接的逻辑关系 只要一个假就是假的 全是真的才是真的

or 是或的运算符 对于or和and的区别在于 or连接的逻辑关系只要一个是真的,那整个表达式就是真的,全部是假的时候才是假的

a=100
if a<0 or a>0:
   print("OK")

上面的if条件也是真的,因为a<0虽然为假但是 a>0为真 由于or的连接 整个condition表达是(a<0 or a>0)就是真的

# in 判断 一个变量是否在一个对象中常被和list连用比如

L=[1,2,3,5,5]
if 1 in L:
   print("OK")
#1在L里面所以返回的是真

#not in 是不在的意思 和in的返回值恰好相反

补充一种3.8的新特性在if条件语句的使用场景内的玩法:海象表达式":="运算符

L=[1,2,3,4]
if (n:=len(L)) >2:
   print(n)
#这句话的意思就是用len(L)给n赋值,但是n的作用域只有if条件里面的部分,这样避免的len()函数多次调用带来的不必要的开销

注:在if条件中的()可写可不写都没有关系,不会影响代码的执行

循环

for循环
1.基于范围的for循环

基于范围的for循环最简单的一个例子:

L=[1,2,3,4,5]
for i in range(len(L)):
   print(L[i])

分析上面的代码每一次拿到的都是i,而i是列表的数据的下标,并且一定不会越界,说明一下range函数,range()函数有两个参数,range(start,end)返回一个[start,end)的数组,从start开始,也可以只传一个参数,就像我们上面的那样,默认从0开始: [0,len(L)) 左闭右开的区间

2.基于内容的for循环

照样从一个例子开始:

L=[1,2,3,4]
for d in L:
   print(d)

在上面的for循环中每一次拿到的是L中的元素从第一个元素开始到最后一个元素,所以相比于第一个拿到的下标而言称为基于内容的for循环

3.基于内容和范围的for循环
L=[1,2,3,4]
for index,data in enumerate(L):
     print(index,data)

采用了enumerate之后 每一次for循环返回的是两个值,一个是元素的下标,一个是元素的值,也可以理解为1和2的结合体[暂时这样理解即可]

4.列表解析式

在第一节得时候谈到list得数据结构,如何快速得初始化一个list,比如你想得到从1-100得所有得元素或许你会这样做:

new_list=[]
for i in range(1,101):
    new_list.append(i)
print(new_list)

python中还可以这样得写法

new_list=[i for i in range(1,101)]

这样得写法是不是简洁了许多,并且程序得可读性更好了,对于这个功能python中叫做list comprehension

根据官方得指定形式使用list comprehension必须采用如下得形式:

[ expression for expr in sequence1
             for expr2 in sequence2 ...
             for exprN in sequenceN
             if condition ]

也就是可以通过多个for循环进行嵌套的使用list comprehension

这个语法仅仅只限于list这种对象吗?

答案当然不是

对于字典来讲来可以这样进行字典的生成:比如我们现在有这样的需求,对于一个字符串"abcdefgh",把每一个字符当作value按照自然数的序数当作key,创建一个字典,你可以这样做:

str="abcdefgh"
dict={i:j for i in range(1,len(str)+1) for j in str }

#dict的输出将是:{1: 'h', 2: 'h', 3: 'h', 4: 'h', 5: 'h', 6: 'h', 7: 'h', 8: 'h'}

在集合中照样也可以这样使用,比如你要选择一个list中的所有不同的元素可以:

list_=[1,1,2,2,3,3,4,4,5,5]
s={i for i in list_}
>>> {1,2,3,4,5}

当然你还可以对你在for循环中选择的元素进行条件的筛选,就像官方给出的最后一个if condition 如果这个condition为真的话才会被选择进去
比如现在我要在一个list中筛选出来所有的大于0的数字那么我可以使用下面的方式:

L=[-1,-2,0,1,2,3,-3,2,0]
out=[i for i in L if i>=0]
>>>[0, 1, 2, 3, 2, 0]
while 循环

while循环类似于if语句 不同于for循环 while循环有循环的起始条件,这个起始条件为真的时候才开始循环,条件变成假的时候终止循环 每一次循环结束后都会检验条件是否为真

i=10
while(i>0)
{
  i=i-1
}

上面的代码在i>0的时候开始进入到while循环,每次进入之后i=i-1被执行,紧接着(i>0)条件判断是否为真为真继续执行,否则跳出.....重复上述过程就是while循环

函数(1)

函数的基本定义方式

在python中定义一个函数的方式的模板如下:

def fun_name(arg):
    return value

其中必须写的是def相当于英文的definite(定义)的意思,直接可以理解为定义一个名为fun_name的函数,其中函数的参数是arg,也可以不只是一个值,可以是多个值,用逗号分割符隔开,返回一个value

指定参数的位置和参数的初始化方式

在python中假如你定义了一个函数:

def fun(a,b):
    print(a,b)
a=10
b=20
print(fun(a,b))

在上面的函数中分析一下执行过程如下:首先你定义了一个名称为fun的函数,这个函数有两个参数:a,b,但是没有返回值:所以最底下的print(fun(a,b))执行完成后输出打印的是None

参数的默认值

依旧是上面的代码 我们考虑下面这样的情况:

def fun(a,b=20):
    print(a,b)

a=20
print(fun(a))

观察和上面的代码不同的地方是我们给参数b增加了一个默认的值,这个默认值可以指定任何的类型就像命名一样,但是有一个潜规则:指定默认值的参数必须在不指定默认值的参数的后面,上面的函数输出是20,20
可以采用只输入没有指定默认值的参数就像上面那样,也可以输入两个参数像这样:

fun(10,12)
#也可以
fun(a=10,b=12)
fun(b=12,a=10)
#用参数的名字指定参数的值

这样a的值初始化为10,b的默认值20被覆盖掉变为12进入到函数中

指定位置的参数传递方式

在python3.8中新加入了一种指定位置传参数的方式,通俗的来讲,就是传递参数的时候只能按照参数的顺序进行传递,不能按照参数的名字进行传递参数也就是说不能使用上面指定的fun(a=10,b=12)的方式进行传递参数,只能fun(10,12)的进行传递,要想让函数中的参数是这样的规则进行调用,初始化函数的语法应该如下:

def fun(a,b,/,c): #在'/'符号之前的所有参数都被设置为按位置传参
    #在'/'之后的参数可以使用fun(1,1,12)(c=12),也可以使用fun(1,1,c=12)的方式
    print(a,b,c)

指定参数的传递方式

在上面调用函数的例子中有一种调用的格式是fun(a=10,b=12)的方式,这种方式就是指定参数的传递,因为用到了参数名字去指定参数的值,在指定参数的传递中
你可以不考虑参数的位置因素,例如fun(a=10,b=12)和fun(b=12,a=10)是相同的效果的传递和函数调用
如何在函数中使用这样的规则局限于一部分的参数:

def fun(*,a,b): #在参数之前用符号'*'表示之后的参数都要按照参数传递
    print(a,b)
fun(a=1,b=3)#正确
fun(b=3,a=1)#正确
fun(1,3)#错误

Question1:说说为什么上面的fun(1,3)是错误的?

小结:

对于一个函数如下

def fun(a,b,/,c,d,*,e,f):
    print(a,b,c,d,e,f)

对于参数a,b而言我们知道参数a,b是按照位置传递的参数,而e,f是按照参数进行传递的参数,那么c,d呢?
c,d是既能按照参数传递也能按照位置传递的参数,也就是说你可以直接fun(1,2,3,4,e=5,f=6)的初始化,也可以
fun(1,2,c=3,d=4,e=5,f=6)的进行初始化,两种规则都适合使用

函数的返回值

函数的返回值通常来讲只有一个,紧跟在return关键字之后:

def fun(a):
    return a
#a可以是任何对象(list tuple 字典 字符串)以及任何的数据类型 

如果函数没有写返回值的话,那么默认的返回值是None

Question2 :如何在函数中返回多个值?写出代码检验你的想法?

函数中的函数(子函数):

先看下面的代码:

def fun():
    def subfun1(x):
        print(x)
    return subfun1
f=fun()
print(f(1))

分析:考虑上面的函数fun()我们定义了一个subfun1(x)的函数,并且调用这个函数之后将subfun1的函数座位返回值返回
然后用f接受这个返回值也就是说此时f=subfun(x)的函数f是这个函数的第二个名字,通过f(1)调用这个函数,print(1)进行输出打印

理论上来讲子函数可以定义任意多个,对于变量的作用域的问题:

def fun():
    x1=10
    def subfun1(x):
        x2=12
        x1=x1+1#错误,不可以使用外部的块的内容 
    x2=x2+1#错误 不能使用子函数块的内容
    return subfun1

也就是说子函数和外部的函数之间是彼此独立的不可以互相沟通

lambda函数

lambda函数作为一种简单的使用的方式,有时候可以当成一种规则的去使用:在有的python的内置函数中可以传递lamda函数作为参数
比如:排序 映射 之类的函数(sorted(),map())都可以传递一个lambda函数
lambda函数有时候也叫做匿名函数

定义方式如下:

f=lambda x,y:x*y #调用方式为
f(1,2) #x=1 y=2 返回1*2
# lambda arg1,arg2...:函数体
#首先写一个lambda 紧接着是参数列表 arg1,arg2.... 在:后跟的是函数体

使用场景

L=[1,1,2,3,4]
print(sorted(L,key=lambda x:abs(3-x)))

按照L中的值和3的距离的大小进行排序,key是一个仅按照参数传递的参数

list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))

对列表[1, 2, 3, 4, 5, 6, 7, 8, 9]中的值都进行x*x的操作

Question3:改写下面的函数为lambda函数:

def fun(x,y,z):
    return x-y+z
def fun2(x):
    return x**2+x*x

函数(2)

Recall

复习一下
[注:]在python中的函数可以当作变量来进行赋值!!!
例子如下:

def fun(name):
    print("I am {}".format(name))
fun("zhenzhen")
f=fun#将函数fun赋值为f
f("xiongxiong")#然后进行调用也是可以进行执行的

回想一下上一次所提到的函数中的函数(也就是子函数),看下面这样的一个例子:

def fun(key):
    def subfun():
        if key=='1':
            print("I am {}".format(key+"号"))
        elif key=='0':
            print("I am {}".format(key+"号"))
    return subfun
f=fun("1")
f()

Question1:上面的写法是否正确,调用了f()之后会发生什么?

装饰器

python 的装饰器是相对于函数而言的,以下网上有一个不友好但是很恰当的比喻如下:
        每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它变得更厚更长,这样一来,它不仅有遮羞功能,还能提供保暖,不过有个问题,这个内裤被我们改造成了长裤后,虽然还有遮羞功能,但本质上它不再是一条真正的内裤了。于是聪明的人们发明长裤,在不影响内裤的前提下,直接把长裤套在了内裤外面,这样内裤还是内裤,有了长裤后宝宝再也不冷了。装饰器就像我们这里说的长裤,在不影响内裤作用的前提下,给我们的身子提供了保暖的功效。


python的装饰器就是形如上面的功能,下面考虑这样的一个简单的场景:假定你现在要写100个函数:

def fun1():
    print("I am duanfeixiong !!!")
    pass
def fun2():
    print("I am duanfeixiong !!!")
    pass
def fun3():
    print("I am duanfeixiong !!!")
    pass
def fun4():
    print("I am duanfeixiong !!!")
    pass
def fun5():
    print("I am duanfeixiong !!!")
    pass
def fun6():
    print("I am duanfeixiong !!!")
    pass
......
def fun100():
    print("I am duanfeixiong !!!")
    pass

上面的100个函数每一个函数都要打印一句相同的字符串"I am duanfeixiong !!!",那么整段代码看起来显得极其的丑陋可读性差,那么对于这样的场景(实际场景往往更加复杂!),就可以使用python的装饰器的形式,我们写下这样的代码:
在老一点版本上可以这样写:

def summary(func):
    def wrapper():
        print("I am duanfeixiong!!!")
        return func()
    return wrapper
def fun():
    '''
    do something
    '''
    pass

#装饰部分
fun=summary(fun)
fun()

分析一下上面代码的执行过程:
首先我们定义了一个summary函数,传递进去的参数是一个函数fun, summary这个函数返回的是wrapper()函数,wrapper函数首先执行打印那句语句然后执行fun()部分,但是这样写法有点问题,如果fun函数有参数比如fun(key)怎么办,一个写法是这样的:

def summary(func):
    def wrapper(key):
        print("I am duanfeixiong!!!")
        return func(key)
    return wrapper
def fun(key):
    '''
    do something
    '''
    print(key)
    pass

#装饰部分
fun=summary(fun)
fun(key)

回忆一下上次提到的函数的子函数以及子函数作为参数进行返回,观察上面的方法不难看出,我们只需要把fun的参数移植到wrapper函数的参数那里就行,这样根据上面所说的就可以进行对被装饰的函数的参数传递

@语法糖

观察上面的函数可以了解到一个共同点都需要在装饰的时候使用一条赋值语句这样有点多余,并且作用域的使用不好把握,要是在整个代码中使用被装饰的函数的话,你不得不在你的代码的最上面使用那条赋值语句,这个时候来了一种新的方式,同样是上面的代码我们只需要如下的写法即可:

def summary(func):
    def wrapper():
        print("I am duanfeixiong!!!")
        return func()
    return wrapper

@summary #直接在函数声明的时候对其进行装饰,和赋值语句是等价的写法
def fun():
    '''
    do something
    '''
    pass

#装饰部分
#fun=summary(fun)可以删除这句话
fun()

生成器和生成函数

重新回到for循环的地方谈一谈基于内容的for循环,我们知道要是对一个list进行遍历,并且每一次拿到list中的值进行打印的话,那么采用下面的语句是再适合不过的:

L=[1,1,2,2,3,4,5,5]
for d in L:
    print(d)

那么可以被for循环直接作用的容易和数据类型有哪些?

  1. 集合类型的容器,例如 list,tuple,set,str,dict
  2. 生成器生成函数
    可以通过for循环直接访问的叫做可迭代对象

需要记住上面两种!!!

对于第一种类型而言我们已经很熟悉了,考虑一下第二种的使用:

生成器

创建方式如下:

L=[x**2 for x in range(100)] #这是一个列表解析式,对0-99的数字平方后放在L中

G=(x**2 for x in range(100)) #这是一个生成器不是tuple

阐述一下上面两种的本质上的区别:

L是一个集合类型的容器list,在初始化的时候已经决定了他的大小是100,而对于G而言并没有,G是一个生成器需要不断调用一个next函数不断返回下一个值,对于空间的开销极其低相比于list

>next(G)
0
>next(G)
1
>next(G)
4
....

也可以被for循环直接作用:

for d in G:
    print(d)

上面的for循环等价于对G不断的使用next()函数

生成函数

依旧是上面的例子和函数$x2$那么如果我们想写一个函数接受一个参数n返回从0到n-1的所有x2的值,那么我们可以这样写:

def fun(n):
    L=[]
    for i in range(n):
        L.append(i**2)
    return L

但是这样的写法太过于繁琐,list对空间的开销比较大,我们可以转而使用生成函数

def fun(n):
    for i in range(n):
        yield i**2
fun(10)#创建了一个生成器
print(type(fun(10)))#查看类型 自己打印一下!!!
next(fun(10))#next函数照样适用
#也可以
for d in fun(10):
    print(d)

yield关键字可以这样理解,在调用了next()函数之后,fun(10)中for循环前进一次,yield可以当作返回值,不同于别的普通函数的是:next()下一次的时候从上一次next()函数终止的位置继续往后执行!!!

面向对象(1)

类的定义

和所有的面向对象语言一样,python也有其面向对象的管理的方式:class,其基本的模型如下写法:

class Myclass:
    def __init__(self):
        pass
    def memfun(self):
        pass    

对于上面的类的基本框架可以看出 多了一个关键字 class 和一个加了下划线的函数init

  1. 其中class 关键字是类的声明也就是说是如下的:
class classname:
  1. init()函数类似于其他语言中的构造函数
class father:
     def __init__(self,name,age):
         self.name=name
         self.age=age

如上所示我们创建了一个father类,这个类的构造函数有三个参数 self,name,age(解释如下):
self:self是对于类中的每一个函数(成员函数构造函数)都要写的一个关键字,写升self之后才可以紧跟着其他的参数
除了self参数之外的所有参数全部当作普通函数的参数进行对待.

在类中定义一个变量供类中所有的成员函数进行访问的话,可以写作,self.name=name,也就是说创建了一个name的变量可以在类中所有的地方访问,如下所示:

class father:
     def __init__(self,name,age):
         self.name=name
         self.age=age
     def get(self):
         print("My father name is {},age is {}".format(self.name,self.age))

我们定义了一个 get()的成员函数,用于打印father类的姓名和年龄,那么在类中成员函数中定义参数的时候,也要加上self关键字之后然后增加其他的参数(和普通函数一样).

实例的创建

创建对象

还是上面的father类,我们考虑对象的创建:

f=father("xiaohua",19)

上面的代码我们创建了一个father对象,第一个参数代表是father的姓名,第二个参数代表的是father的年龄,这样就创建了一个对象,注意初始化对象的时候参数的使用和类中的__init__()函数的参数是一样的,这样也就解释了为什么__init__()函数是构造函数.

对象的使用

假如我们对father类增加了一些功能如下:

class father:
     def __init__(self,name,age):
         self.name=name
         self.age=age
     def get(self):
         print("My father name is {},age is {}".format(self.name,self.age))
     def addage(self): #年龄增加的模块
         self.age+=1

#定义一个father对象
f=father("xiaohong",19)

#调用类中的方法采用'.'的形式进行调用
f.get() #打印信息

f.addage() #father年龄增加

类的权限问题

假如我们定义了一个类 girlfriend 类中有些东西不能再类的使用中被外部用户看见,比如 girlfriend类中有一个生气的属性,这个属性可以被外部对象看见, 但是girlfriend 类中 生气的原因的属性就不能被外部对象可见,但是怎么进行沟通呢?假如有一个boyfriend类,这个类有一个 Gauss函数用来猜测 girlfriend 为什么生气的原因,那么我们怎么把girlfriend类的生气原因的属性定义为 只对类自己可见呢?

class girlfriend:
    def __init__(self,name,age,p):
        self.name=name
        self.age=age
        self.prop=p #生气属性值
        self.__why="happy" #如果不生气就是happy
    def mad(self):
        #女朋友不生气的原因只有不到0.1,0.9的时候都在生气
        if self.prop>0.1:
            return True
        else:
            return False
    #为什么生气
    def __why(self):
        if self.mad():
            #因为我也不知道所以返回None
            return None
        else:
            return self.__why

我们使用一下这个类:

    
g=girlfriend("zhenzhen",19,0.01)

g.mad() #生气了

#查看原因
#g.why()#error 因为原因是不可以见的

#查看是否开心

print(g.__why1) #error 生气的原因都不可见 怎么可见开心的原因

总结一下:对于一个类想定义对用户不可见的函数和变量只需要在想定义的变量和函数名称前写上 '__'即可如上所示

[注:]

由于python中不同于c和c++的有垃圾回收机制,所以没有析构函数一说!!(但是会有__del__()函数用于自定义的操作下一节会说到)

## 面向对象(2)

继承和多态

继承

对于python中的类的使用在py2的时候一个普通的类的写法如下:

class Myclass(object):
      pass

其中object是Myclass应该继承的类的名字,类的继承可以导致多态,类本身的特性导致了封装

那么继承的写法如何?,如下所示:

class A:
    def __init__(self):
        pass
    
class B(A):
    def __init__(self):
        super(B,self).__init__()

上面的例子我们首先定义了一个类A,这个类A是一个普通的类A,然后定义了一个类B,这个类B继承于类A,写法只是在类B的名字上加上一个括号写上需要被继承的类的名字即可,并且在继承类的init函数中加上super语句用于对基类部分进行初始化

多态

多态的发生有下面的条件满足即可:

  1. 发生继承
  2. 子类重写父类的方法(子类中的方法函数和父类中的名字参数全部一致)

看一个例子:

#食物类
class food:
     def get(self):
       print("I am food class")
#继承food类
class nood(food):
#重写父类方法get
    def __init__(self):
        super(nood,self).__init__()
    def get(self):
       
       print("I am nood class")
class meat(food):
    def __init__(self):
        super(meat,self).__init__() 
    def get(self):
        print("I am meat class")

在上面我们定义类的继承关系如下:

类nood继承于food 类meat继承于food 并且两个类都重写了父类的方法函数get()

我们定义一个函数

def fun(s):
    s.get()
food f
nood n
meat m
fun(f)
fun(n)
fun(m)

可以观察到打印的是不同的信息,fun(f)打印的是"I am food class",fun(m)打印的是"I am meat class",不同的对象打印的是不同的东西,但是这样你会说,那我只需要定义两个不同的类,然后再里面定义两个相同的名字的函数就可以了哇.

要回答上面的问题,得考虑类得继承使得子类对父类得成员访问得问题:
前面已经说到了怎么在一个类中定义一个私有变量:(变量名前加上双下划线即可)

看下面得一个例子:

class A:
    def __init__(self,a,b):
        self.__a=a
        self.b=b
    def get(self):
        print("I am class A")
class B(A):
    def __init__(self,a,b):
        super(B,self).__init__(a,b)

    def get(self):
        print("I am class B")

    def fun(self):
        print(self.__a)#错误 __a是A中的私有成员
a=A(1,2)
b=B(1,2)
print(a.__a)#错误用户不能访问类中的私有成员
print(b.__a)#错误 理由同上

在上面得例子中我们把__a定义为私有变量,在B中是不可访问得,但是b是公有的所以会被继承下去,所以我们可以在B类中访问类A的公有部分,但是不可以访问私有的部分!!!
但是如果要在外面使用的时候查看对象的私有成员的部分,可以采用如下的方式:

print(A(1,3)._A__a)# '_类名__变量'的方式进行打印

注:在python中所有变量和方法一律默认为public的


类的其他功能方法

对于python中的变量来讲比如 a=10,b=5,你可以使用a+b(这会返回15),a*b,a-b之类的四则运算的操作符,类似的还有取余的运算和list的[index]返回对应的位置的值,但是如果对于自定义对象而言呢?,一个自定义的类初始化的对象之间如何使用这些个操作符来完成一系列操作呢?答案是这需要类的设计者定义一些方法去自定义对象之间的加法,减法,乘法等的运算,(如果你有学过c++,这和c++的运算符重载有着相同的目的和相似的操作,你只需要将self看作this指针即可)


考虑下面的一个场景,你定义了一个向量类比如说是vector,你创建了两个向量的对象分别是
$v_1,v_2$那么你要定义向量的加法乘法比如$v_1+v_2,v_1-v_2$如何定义,python中有类似于__init__()的一些功能函数实现这些复杂的操作:

init: 构造函数,在生成对象时调用
del : 析构函数,释放对象时使用
repr : 打印,转换
setitem : 按照索引赋值
getitem: 按照索引获取值
len: 获得长度
cmp: 比较运算
call: 调用
add: 加运算
sub: 减运算
mul: 乘运算
div: 除运算
mod: 求余运算
pow: 幂
上面的函数都有类似于__init__()双下划线的形式,并且名字不能发生改变.

向量类的设计

下面我们开始着手定义一个向量类:

class vector:
    def __init__(self,x):
        '''
        type(x)=list
        '''
        self.v=x 
    def __add__(self,y):
        #相当于重载'+'运算符 
        #self是加号左边的对象,y是加号右边的对象
        #两边的对象都是vector类型的
        if len(self.v)!=len(y.v):
            return "Error!!!"
        return vector([self.v[i]+y.v[i] for i in range(len(self.v))]) 
        #返回值是vector类型
        #也可以返回其他类型的值,例如list
v1=vector([1,2,3])
v2=vector([1,2,3])
print((v1+v2).v)

在上面我们定义了__add__()函数使得$v_1+v_2$变得可能,并且加完之后返回一个新的vector对象

output: [2,4,6]

向量之间不仅仅存在加法而且还存在乘法:(定义如下的函数添加到类中:)

def __mul__(self,y):
    if len(self.v)!=len(y.v):
            return "Error!!!"
    #向量的内积运算
    return sum([self.v[i]*y.v[i] for i in range(len(self.v))])
print(v1*v2)

output:14

如果要打印向量的长度如何打印,一种方法是定义一个getlen()的函数:

def getlen(self):
    return len(self.v) #返回list的长度

但是这个的实质其实是调用的python内置的len()函数用于返回长度,如果你直接使用:

print(len(v1))

会报错如下:

object of type 'vector' has no len()

也就是说这个对象没有len的属性,我们定义如下的函数在类中:

def __len__(self):
    return len(self.v)
print(len(v1))

增加了__len__()函数之后的代码就不会报错了,并且可以正确的返回向量的长度

还有问题是如果我们需要按照索引修改向量的值,或者根据索引返回向量的值怎么做?

在类中增加函数如下:

#根据索引修改值
def __setitem__(self,index,value):
        self.v[index]=value
#根据索引返回值
def __getitem__(self,index):
        return self.v[index]

观察上面两个函数:

  1. setitem 有两个参数 一个是index 一个是value,也就是说你可以根据index 设置value 你可以理解为数组的形式,给对象增加了一'[]'运算符,此时你可以用v1[index]=value来对向量的每一个分量的值赋值操作

  2. getitem 只有一个参数index 如果你将这个函数理解为普通函数的话,那么不同的是调用方法的()运算符变为了[] 根据你输入的index设置返回值

v1=vector([1,2,3])
v1[2]=4 #setitem 函数的作用 1为index  value=4
print(v1[0]) #getitem 的作用 0为index

全部的vector类的代码如下:

class vector:
    def __init__(self,x):
        '''
        type(x)=list
        '''
        self.v=x 
    def __add__(self,y):#相当于重载'+'运算符
        #self是加号左边的对象,y是加号右边的对象
        #两边的对象都是vector类型的
        if len(self.v)!=len(y.v):
            return "Error!!!"
        return vector([self.v[i]+y.v[i] for i in range(len(self.v))]) #返回值是vector类型
        #也可以返回其他类型的值,例如list
    def __mul__(self,y):
        if len(self.v)!=len(y.v):
            return "Error!!!"
        return sum([self.v[i]*y.v[i] for i in range(len(self.v))])
    def __len__(self):
        return len(self.v)
    def __setitem__(self,index,value):
        self.v[index]=value
    def __getitem__(self,index):
        return self.v[index]

括号运算符的重载:

考虑一个类我们希望他的对象可以像c++的仿函数那样的性质,也就是说有一个A的对象 a 我们可以通过括号运算符 a()来完成一些操作的话,我们可以使用__call__()函数进行实现:

class A:
    def __call__():
        print("calling __call__() function!!!")
a=A()
a()

运行上面的代码即可调用类A中的__call__()函数,这有点类似于c++中的括号运算符的重载

一些常用的内置函数操作

迭代器的功能和for循环,生成器的区别

可迭代对象

  1. 由前面我们知道 for循环可以作用的对象有 1. 集合类型数据 2. 生成器和生成函数(回想一下生成器怎么返回到下一个对象?),在python中凡是可以被for循环直接作用的叫做可迭代对象,也就是Iterable 对象(也就是说凡是可以被for循环基于内容的作用的对象都是可迭代对象)
  1. 凡是可以调用next()函数作用获得下一个元素的都是迭代对象(Iterator)

回想生成器和生成函数,联系所提到的可迭代对象和迭代对象的区别不难看出有如下的异同之处:

  1. Iterable对象 不一定是 Iterator对象(例如 list tuple ... 都可以被for循环直接作用,但是不可以被next()函数直接作用)
  2. 生成器和生成函数既是Iterable对象还是Iterator对象

内置函数的使用

  1. max()函数

这个函数的调用方法有两种,第一种是传入一个可迭代对象(Iterable),第二种是传入任意数量的参数(数字两个或者两个以上),返回对象或者是参数中的最大的那一个,具体的使用方式如下:

#Method1
L=[1,2,3,4,5] #list是一个Iterable但是不是Iterator

max(L)
#返回L中最大的那个元素5

#Method2
max(1,2,3,4,5,6,7,8,9)
#传入指定的大小的参数,返回9

使用场景2 有一个可选的参数key
例如下面有一个字典,要对这个字典按照字典的key或者是value进行排序

dit={"a":1,"b":2,"c":3}
max(dit,key=dit.get)
#这将按照字典的值进行排序并返回value最大的那个值的key

注:key参数的传递方式可写可不写,并且key是按参数传递的参数
key传递函数
考虑这样一个场景,有一个list,我要找到他的平方项最大的那个数字是多少

L=[-10,1,2,3,4,0]
max(L,key=lambad x:x**2)
#key是一个lambda函数 用于指定排序规则是x^2,这将返回-10,并不会改变list原本的值
  1. min()函数 调用方式和max()函数调用方式一致
  1. len()函数

len()用于返回对象的长度 对象可以是集合和序列型数据,例如list dict set string tuple 之类的注意:没有生成器!!!!


下面说说为什么没有生成器?(个人理解)
比方说我们定义了下面的生成器:

L=(x for x in range(10))

乍看之下 这貌似长度是10,但是生成器是调用next()函数作用的时候才到下一个,也就是说

next(L) #for循环前进一次暂停
#output:0
next(L) #从上一次终止的位置继续循环
#output:1

也就是这个东西的长度其实是不确定的,你要使用生成器多少的值,这归结于你的代码,并不是内部属型,运行时才确定你使用了多少次生成器,所以不能使用len()函数去确定

再一个使用len函数的场景,在我们自定义的对象中,也就是你的类里面只要有___len___()函数,你就可以用len()方法去作用你的对象也就是下面的形式:

class Myclass:
     def __init__(self):
        pass
     def __len__(self):
	    pass
  1. sorted()函数
    官方给出的参数列表如下:
    sorted(iterable,*,key=None,reverse=False)

解释如下: 第一个参数需要传入的是一个可迭代对象,key参数传递一个用于排序的比较规则默认为None,reverse默认为False,也就是说默认为从小到大进行排序,如果设置为True 则从大到小进行排序

例子:

L=[1,3,7,0,2,1]
sorted(L)
#输出从小到大的排序
sorted(L,reverse=True)
#输出从大到小的paix
#关键字key的使用

L1=[-10,5]
sorted(L1,key=lambda x:x**2)
#按照平方之后的数字对list中的每一个数字比较大小然后进行排序
#输出[5,-10]

  1. map()函数

map函数的参数规范为:map(function,iterable,...)
第一个参数是函数,第二个参数是可迭代对象,并且可以传入多个可迭代对象
看下map()函数的使用

L=[1,2,3,4,5]
def fun(x):
    return x**2
print(list(map(fun,L)))
#因为map对象不可以打印转为list之后进行打印

传入多个可迭代对象的时候的使用

L1=[1,2,3,4,5]
L2=[1,2,3,4,5]
def fun(x,y):
    return x+y
print(list(map(fun,L1,L2)))
#output: [2,4,6,8,10]

观察上面的代码 fun()函数接受两个参数x,y这说明x和y分别来自不同的Iterable对象L1和L2,那么map()函数每一次在L1和L2中去一个值,x来自L1,y来自L2,return x+y并且输出.

[注意事项:]

  1. 传入的Iterable对象的个数要和函数的参数的个数保持一致.
  2. 如果传入的两个Iterable对象的长度不一致怎么办?答案是在函数运行完比较短的那个Iterabel对象的时候就终止map
  1. 更高级的玩法slice(切片) 对象---slice()函数

官方给出的参数是 slice(start,stop,step)

start:起始的位置,stop终止的位置,step代表前进的步长

L=[x for x in range(50)]
#或许这些数字中的偶数
'''
分析一下如何获得:
选择L中的元素应该是从第一个开始
每一次隔着一个选择,一个接着一个连续的时候
步长step=1,那么我们只需要把step设置为2即可
'''
s=slice(0,len(L),2)#创建从0到len(L)的切片对象,每一次的step是2
print(L[s])#使用切片对象的方式是L[s]
#等价于使用L[0:len(L):2]但是这样不免使得你的代码可读性降低
  1. bin函数: 输入一个整数(可以是正可以是负的),返回他的二进制形式的字符串"0bxxxxx"
  1. complex()函数用于初始化一个复数的对象,函数参数为:complex(real,im) real:实部,im:虚部 返回一个复数类型 real+im*j
c=complex(1,2)
#c=1+2j 是一个复数
  1. round()函数---浮点数的精度问题

在python中考虑一个浮点数f=0.1236222222222,你需要将这个浮点数精确到2位的小数那么你可以考虑使用round()函数

用法如下所示:

f=0.123456 #一个6位的浮点数

round(f,2)#精确到小数点后面两位
#return的是一个两位的浮点数 也就是0.12
#对于在中间部分得精度舍去得时候,比如说2.5,1.5,3.5之类得,round会返回离他得整数部分最接近得那个偶数
f1=2.5
f2=1.5
round(f1,0)
round(f2,0)
>>>2.0
>>>2.0
#并且round()函数还可以传递得是负数,例如下面得这样:
f3=123456
round(f3,-1)
>>>123460
round(f3,-2)
>>>123500
round(f3,-3)
>>>123000
#可以看出负数得话是从小数点向左边偏移,并且对应位舍入或者加一之后对应位所在得位置到小数点所有得位变为0
  1. zip()函数 对多个可迭代对象进行打包处理

传入的参数: 多个可迭代对象
输出: 从每个可迭代对象中选择一个组成一个元组,按照最短的可迭代对象的长度生成元组的个数,看下面的例子:

L0=[1,2,3,4]
L1=[1,2,3,4]
L2=[1,2,3,4]
print(list(zip(L0,L1,L2))) #类似于map函数的操作,zip函数返回的是一个zip对象
#,转化为list进行输出


#output: [(1,1,1),(2,2,2),(3,3,3),(4,4,4)]
#依次从每一个Iterable对象中选择一个元素进行打包为tuple

  1. set()函数
    set()函数作用在一个Iterable对象上面 返回一个新的set对象,这个新的set对象的值由Iterable 对象中的不相同的元素组成
L=[1,2,2,3,3,4,5]

print(set(L))

#output: 1,2,3,5,5

  1. sum() 函数

sum函数的参数有两个: sum(Iterable,start=0)

需要传入的是一个Iterable对象 默认从0开始加,也就是start=0的意义

L=[1,2,3,4,5]
sum(L) #输出15

sum(L,start=12)
# 输出27 从12开始求和 12+15=27

posted @ 2020-12-04 14:25  差三岁  阅读(189)  评论(0编辑  收藏  举报