一、开发编辑器
1. pycharm
2. IDLE(Python自带软件)
方法:Microsoft Store搜索 Python 安装
二、数据类型
- Python中有以下几种主要的数据类型:
数字(Numbers)、
字符串(Strings)、
布尔值(Boolean)、
空值(None)、
列表(Lists)、
元组(Tuples)、
字典(Dictionaries)、
集合(Sets)。
1. 数字(Numbers)
- 在Python中,number数据类型可以分为以下几种:整数、浮点数、复数。
# 整数
num1 = 42 #【注:python定义变量的方式很简单,通过变量名 = 数据,即可定义一个变量】
num2 = 0b0110 #【注:在Python中,二进制整数使用前缀0b表示】 # 浮点数
num3 = 3.14159 # 复数
num4 = 3+4j
整数、浮点数可以直接进行四则运算:
# 加法
num1 = 10
num2 = 0.5
result = num1 + num2
print(result) # ==> 10.5
# 减法
result = num1 - num2
print(result) # ==> 9.5
# 乘法
result = num1 * num2
print(result) # ==> 5.0
# 除法
result = num1 / num2
print(result) # ==>20.0
取模运算
print(3 % 2) # ==> 1
print(33 % 10) # ==> 3
print(99 % 30) # ==> 9
地板除
- Python除了普通除法以外,还有一个特殊的除法被称为地板除,对于地板除,结果会忽略纯小数的部分,得到整数的部分,地板除使用//进行。
10//4 # ==> 2
10//2.5 # ==> 4.0
10//3 # ==> 3
小数点位数
- 使用Python计算小数的时候,经常需要保留小数点后若干位,可以使用round()函数来处理,这里先了解round的调用方式,使用两个参数,第一个是需要保留小数点位数的数值,第二个是保留的位数。
num = 10 / 3
print(num) # ==> 3.3333333333333335
# 使用round保留两位小数
round(num, 2) # ==> 3.33
2. 字符串(Strings)
#定义一个字符串str变量
str = "Hello, World!"# 字符串模板
template = 'Hello {}'
# 模板数据内容
world = 'World'
result = template.format(world)
print(result) # ==> Hello World#读取字符串某个值
s = 'ABC'
a = s[0] # 第一个
b = s[1] # 第二个
c = s[2] # 第三个
print(a) # ==> A
print(b) # ==> B
print(c) # ==> C#字符串切片
s1 = 'ABCDEFGHIJK'
abcd = s1[0:4] # 取字符串s1中的第一个字符到第五个字符,不包括第五个字符
print(abcd) # ==> ABCD
3. 布尔值(Boolean)
b1 = True #【在Python中,布尔值True和False都是关键字,首字母需要大写】
b2 = False #【布尔值可以用and、or和not运算(注意and,or,not都是Python语言本身的关键字)】
与运算:只有两个布尔值都为 True 时,计算结果才为 True。
或运算:只要有一个布尔值为 True,计算结果就是 True。
非运算:把True变为False,或者把False变为True:
#举例
True and True # ==> True
True and False # ==> False
True or True # ==> True
True or False # ==> True
not True # ==> False
not False # ==> True
4. 空值(None)
# 空值
n = None # 【在Python中,None表示空值,它是一个特殊的类型,叫做NoneType】
5. 列表(Lists)
- 列表(list)是一种有序的容器,放入list中的元素,将会按照一定顺序排列。构造list的方法非常简单,使用中括号[]把需要放在容器里面的元素括起来,就定义了一个列表。
scores = [45, 60, 75, 86, 49, 100]
names = ['Alice', 'Bob', 'David', 'Ellena'] # 注意,字符串元素仍需要引号
L = ['Alice', 66, 'Bob', True, 'False', 100]
- 列表是有序的,因此我们可以按顺序访问列表中的元素。
L = ['Alice', 66, 'Bob', True, 'False', 100]
for item in L:print(item)#获取L中第二个元素
print(L[1]) #66#使用-1来获取最后一个元素
print(L[-1]) #100#使用索引的方式访问列表时,一定要特别注意不要越界
print(L[10]) # IndexError: list index out of range#列表和字符串一样,也支持切片
print(L[0:2]) #['Alice', 66]
- 向list添加新的元素
names = ['Alice', 'Bob', 'David', 'Ellena']
names.append('Candy') # append()方法:追加到列表的末尾
print(names) # ==> ['Alice', 'Bob', 'David', 'Ellena', 'Candy']
names.insert(2, 'LBJ') # insert()方法需要两个参数,分别是需要插入的位置,以及需要插入的元素
print(names) # ==> ['Alice', 'Bob', 'LBJ', 'David', 'Ellena', 'Candy']
- 从list删除元素
L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
name = L.pop() # pop()方法默认删除列表的最后一个元素,并返回
print(name) # ==> Ellena
print(L) # ==> L = ['Alice', 'Bob', 'Candy', 'David']
name2 = L.pop(2) # pop()还可以接收一个参数,指定需要删除的元素的位置
print(L) # ==> L = ['Alice', 'Bob', 'David']
- 替换list中的元素
L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
L[2] = 'Canlina'
print(L) # ['Alice', 'Bob', 'Canlina', 'David', 'Ellena']
6. 元组(Tuples)
- 元组(tuple)和list一样,也是一个有序容器,在元组中,同样可以包含0个或者多个元素,并且也支持索引访问、切片等操作。
- 定义元组的方式是使用小括号()将元组内的元素括起来。
T = ('Alice', 'Bob', 'Candy', 'David', 'Ellena')
# 通过下标的方式访问元素
print(T[0]) # ==> Alice
print(T[4]) # ==> Ellena
# 切片
print(T[1:3]) # ==> ('Bob', 'Candy')
- tuple和list不一样的是,tuple是固定不变的,一旦变成tuple,tuple中的每一个元素都不可被改变,同时也不能再往tuple中添加数据,而list是可以的。【请注意,元组的这个特性是非常重要的,在运行上tuple的性能是list的数倍。】
T = ('Alice', 'Bob', 'Candy', 'David', 'Ellena')
# 替换元素
T[1] = 'Boby' # TypeError: 'tuple' object does not support item assignment
- 元组类型和列表类型相互转换
L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
T = tuple(L)
print(T) # ==> ('Alice', 'Bob', 'Candy', 'David', 'Ellena')
L2 = list(T)
print(L2) # ==> ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
- tuple元素的其他方法
(1)count:用来统计tuple中某个元素出现的次数。
T = (1, 1, 2, 2, 3, 3, 1, 3, 5, 7, 9)
print(T.count(1)) # ==> 3
print(T.count(5)) # ==> 1#对于不存在的元素,count方法不会报错,而是返回0,这是合理的,因为元组里面有0个不存在的元素。
print(T.count(10)) # ==> 0
(2)index:可以返回指定元素的下标,当一个元素多次重复出现时,则返回第一次出现的下标位置。
T = (1, 1, 2, 2, 3, 3, 1, 3, 5, 7, 9)
T.index(9) # ==> 10
T.index(5) # ==> 8
T.index(1) # ==> 0 # 多次出现,返回第一次出现的位置#注意,index()方法和count()方法不一样,当指定的元素不存在时,使用index()方法Python会报错。
T.index(100)
# 报错 ValueError: tuple.index(x): x not in tuple
- 要定义只有一个元素的tuple,需要在元素后面添加一个逗号
T = (1, )
print(T) # ==> (1, )
- tuple的元素也可以是tuple
T = ((1+2), ((1+2),), ('a'+'b'), (1, ), (1,2,3,4,5))
print(len(T)) # 5
- 对于tuple,它和list一个最大的不同点就是tuple是不可变的,tuple里面的元素,也是不可替换的。但是这针对的是仅包含基础数据类型(数字类型、布尔类型、字符串类型)的数据,对于组合数据类型,则不受这个约束。
T = (1, 'CH', [3, 4])
L = T[2]
print(L) # ==> [3, 4]
# 尝试替换L中的元素
L[1] = 40
print(L) # ==> [3, 40]
print(T) # ==> (1, 'CH', [3, 40])
7. 字典(Dictionaries)
- 在dict中,每一项包含一个key和一个value,key和value是一一对应的
(1)读取dict元素
d = {'Alice': 45,'Bob': 60,'Candy': 75,'David': 86,'Ellena': 49,'Gaven': 86
}
print(d['Bob']) # ==> 60
print(d['Alice']) # ==> 45
print(d['Dodo']) # 报错:KeyError: 'Dodo'# get方法读取【推荐】
# print(d.get('Dodo')) # ==> None 【dict本身提供get方法,把key当作参数传递给get方法,
# 就可以获取对应的value,当key不存在时,也不会报错,而是返回None】
(2)添加dict元素
d = {'Alice': 45,'Bob': 60,'Candy': 75,'David': 86,'Ellena': 49
}
d['Alice'] = 100 # Alice存在,将被替换
d['Dodo'] = 88
d['Mimi'] = [72, 73]
d['Mimi'].append(75)
print(d)
(3)删除dict元素
- pop()方法需要指定需要删除的元素的key,当key不存在时,同样会引起错误。
d = {'Alice': 45,'Bob': 60,'Candy': 75,'David': 86,'Ellena': 49
}
d.pop('Alice')
print(d)
d.pop('LBJ') # 报错 KeyError: 'LBJ'
(4)dict的特点
- 查找速度快
- dict的第一个特点是查找速度快,无论dict有10个元素还是10万个元素,查找速度都一样。而list的查找速度随着元素增加而逐渐下降。
- 不过dict的查找速度快不是没有代价的,dict的缺点是占用内存大,还会浪费很多内容,list正好相反,占用内存小,但是查找速度慢。
- key不可变
tuple是不可变的,list是可变的,因此tuple可以作为dict的key,但是list不可以作为dict的key,否则将会报错。
d = {'Alice': 45,'Bob': 60,'Candy': 75,'David': 86,'Ellena': 49
}
key = (1, 2, 3) # 以tuple作为key
d[key] = True
print(d) # {'Alice': 45, 'Bob': 60, 'Candy': 75, 'David': 86, 'Ellena': 49, (1, 2, 3): True}
key2 = [1, 2, 3]
d[key2] = True # 报错 TypeError: unhashable type: 'list'
- 遍历dict
遍历dict有两种方法, 第一种是遍历dict的所有key,并通过key获得对应的value。
d = {'Alice': 45,'Bob': 60,'Candy': 75,'David': 86,'Ellena': 49
}
for key in d: value = d[key]if value > 60:print(key, value)
# ==> Candy 75
# ==> David 86
第二种方法是通过dict提供的items()方法,items()方法会返回dict中所有的元素,每个元素包含key和value。
for key, value in d.items():if value > 60:print(key, value)
# ==> Candy 75
# ==> David 86
(5)dict的其他方法
- 获取dict的所有key
dict提供keys()函数,可以返回dict中所有的key。
d = {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]}
for key in d.keys():print(key)
# ==> Alice
# ==> Bob
# ==> Candy
- 获取dict所有的value
dict提供values()函数,可以返回dict中所有的value。
d = {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]}
for key in d.values():print(key)
# ==> [50, 61, 66]
# ==> [80, 61, 66]
# ==> [88, 75, 90]
- 清除所有元素
dict提供clear()函数,可以直接清除dict中所有的元素。
d = {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]}
d.clear()
print(d) # ==> {}
8. 集合(Sets)
- set和list类似,拥有一系列元素,但是set和list不一样,set里面的元素是不允许重复的,并且,set里面的元素是没有顺序的。
s = set([1, 4, 3, 2, 5, 4, 2, 3, 1])
print(s) # ==> set([1, 2, 3, 4, 5])
(1)读取set元素
由于set里面的元素是没有顺序的,因此我们不能像list那样通过索引来访问。访问set中的某个元素实际上就是判断一个元素是否在set中,这个时候我们可以使用in来判断某个元素是否在set中。
s = set([1, 4, 3, 2, 5, 4, 2, 3, 1])
print(1 in s) #True
print(6 in s) #False
(2)添加set元素
names = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
name_set = set(names)
name_set.add('Gina') #add:逐个添加
print(name_set) # ==> set(['Gina', 'Alice', 'Candy', 'David', 'Ellena', 'Bob'])new_names = ['Hally', 'Isen', 'Jenny', 'Karl']
name_set.update(new_names) #update:批量添加
print(name_set) # ==> set(['Gina', 'Karl', 'Bob', 'Hally', 'Jenny', 'Alice', 'David', 'Isen', 'Ellena', 'Candy'])
(3)删除set元素
set提供了remove()方法允许我们删除set中的元素。如果remove的元素不在set里面的话,那么将会引发错误。
name_set = set(['Jenny', 'Ellena', 'Alice'])
name_set.remove('Jenny')
print(name_set) # ==> set(['Ellena', 'Alice'])
(4)set的其他方法
- 不会报错的删除方法discard()
name_set = set(['Jenny', 'Ellena', 'Alice'])
name_set.discard('LBJ')
print(name_set) # ==> set(['Jenny','Ellena', 'Alice'])
- 清除所有元素的方法clear()
和dict一样,set也提供了clear()方法,可以快速清除set中的所有元素。
name_set = set(['Jenny', 'Ellena', 'Alice'])
name_set.clear()
print(name_set) # ==> set([])
- 集合的子集和超集
set提供方法判断两个set之间的关系,比如两个集合set,判断其中一个set是否为另外一个set的子集或者超集。
s1 = set([1, 2, 3, 4, 5])
s2 = set([1, 2, 3, 4, 5, 6, 7, 8, 9])
# 判断s1是否为s2的子集
s1.issubset(s2) # ==> True
# 判断s2是否为s1的超集
s2.issuperset(s1) # ==> True
- 判断集合是否重合
isdisjoint:用于检查两个集合是否完全独立,即它们没有任何共同的元素。
set1 = {1, 2, 3}
set2 = {4, 5, 6}
print(set1.isdisjoint(set2)) # 输出:True set3 = {2, 4, 6}
print(set1.isdisjoint(set3)) # 输出:False
【扩展】:使用type,可以区分不同的数据类型
print(type(3.1415926))
print(type('Learn Python in imooc.'))
print(type(100))
print(type(0b1101))
print(type([1,2,3]))
'''
结果:
<class 'float'>
<class 'str'>
<class 'int'>
<class 'int'>
<class 'list'>
'''
三、循环控制语句
1. if-else相关:
score = 59
if score < 60:print('抱歉,考试不及格')
else:if score >= 90:print('恭喜你,拿到卓越的成绩')else:if score >= 80:print('恭喜你,拿到优秀的成绩')else:print('恭喜你,考试及格')
可以使用if-elif-else语句来简化以上的逻辑。其中elif就是else if的意思。
score = 59
if score < 60:print('抱歉,考试不及格')
elif score >= 90:print('恭喜你,拿到卓越的成绩')
elif score >= 80:print('恭喜你,拿到优秀的成绩')
else:print('恭喜你,考试及格')
2. for循环
s = 'ABCD'
for item in s:print(item) # 注意缩进
3. while循环
- 计算1~100的和
num = 1
sum = 0
while num <= 100:sum = sum + num # 注意缩进num = num + 1 # 注意缩进
print(sum) # ==> 5050
- 计算0~1000以内,所有偶数的和
num = 1
sum = 0
while True:if num > 1000:breakif num % 2 == 0: sum = sum + numnum = num + 1
print(sum) # 250500
- 输出字符串s中第10个以后的字符
s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
num = 1
for ch in s:if num < 10:num = num + 1continue # 当num < 10时,跳过后续循环代码,继续下一次for循环print(ch)num = num + 1
4. 嵌套循环
s1 = 'ABC'
s2 = '123'
for x in s1:for y in s2:print(x + y)
'''
结果:
A1
A2
A3
B1
B2
B3
C1
C2
C3
'''
四、函数
1. 定义函数
- 在Python中,定义一个函数要使用 def 语句,依次写出函数名、括号()、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用 return 语句返回。
def fn(x):if x >= 0:return xelse:return -x
result = fn(1)
print(1)
2. 递归函数
- 计算n的阶乘
def fact(n):if n==1:return 1return n * fact(n - 1)
res = fact(5)
print(res) #120
我们可以拆解fact(5)计算的详细逻辑:
===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120
五、面向对象编程
1. 类的定义与实例化
class Person(object): pass
xiaohong = Person()
xiaoming = Person()
-
在Python中,pass是一个空操作语句,意思是什么都不做。pass常常用在定义一个空的类、函数、循环或条件语句等场合。
-
在以上的代码中,pass不能删除。pass是Python语法的一部分,用于定义一个空的类(如Person)。如果删除pass,Python解释器会报错,因为这不符合Python的语法规则。
-
当你创建类的实例时,如xiaohong = Person()和xiaoming = Person(),你实际上是在调用Person类的构造方法(如果定义了的话),然后创建了两个新的Person对象。这些对象是独立的,它们的属性不会互相影响。
-
如果你删除pass,你需要在类中定义一个构造方法(使用__init__方法),并在这个构造方法中初始化你想要的属性。例如:
class Person(object):def __init__(self, name, sex, age):self.name = nameself.sex = sexself.age = age
xiaoming = Person('Xiao Ming', 'boy', 13)
xiaohong = Person('Xiao Hong', 'girl', 14)
print(xiaohong.name) # Xiao Hong
print(xiaohong.sex) # girl
print(xiaohong.age) # 14
# 但当访问不存在的属性时,依然会报错
print(xiaohong.birth)
- 以上需要注意的是,init() 方法的第一个参数必须是 self(此处self不能省略,self 也可以用别的名字,但建议使用习惯用法),后续参数则可以自由指定,和定义函数没有任何区别。定义类后,就可以相应的实例化对象了,需要注意的是,在实例化的时候,需要提供除self以外的所有参数。
2. 类属性
在前面,实例对象绑定的属性只属于这个实例,绑定在一个实例上的属性不会影响其它实例;同样的,类也可以绑定属性,但是类的属性不属于任何一个对象,而是属于这个类。如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个!也就是说,实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。
定义类属性可以直接在 class 中定义,比如在Animal类中,加入地域的类属性:
class Animal(object):localtion = 'Asia'def __init__(self, name, age):self.name = nameself.age = age
在上面的代码中,localtion就是属于Animal这个类的类属性,此后,通过Animal()实例化的所有对象,都可以访问到localtion,并且得到唯一的结果。
dog = Animal('wangwang', 1)
cat = Animal('mimi', 3)
print(dog.localtion) # ==> Asia
print(cat.localtion) # ==> Asia
# 类属性,也可以通过类名直接访问
print(Animal.localtion) # ==> Asia
类属性也是可以动态添加和修改的,需要注意的是,因为类属性只有一份,所以改变了,所有实例可以访问到的类属性都会变更:
Animal.localtion = 'Africa'
print(cat.localtion) # ==>Africa
print(dog.localtion) # ==>Africa
- 案例:请给 Animal类添加一个类属性 count,每创建一个实例,count 属性就加 1,这样就可以统计出一共创建了多少个 Animal的实例。
class Animal():count = 0def __init__(self,name,age):Animal.count+=1 # 每创建一个实例,count 属性就加 1self.name = nameself.age = age
dog = Animal('dog',2)
cat = Animal('cat',3)
print(Animal.count) #2
3. 类属性和实例属性的优先级
可以看到,属性可以分为类属性和实例属性,那么问题就来了,如果类属性和实例属性名字相同时,会怎么样?
class Animal(object):localtion = 'Asia'def __init__(self, name, age, localtion):self.name = nameself.age = ageself.localtion = localtion
dog = Animal('wangwang', 1, 'GuangDong')
cat = Animal('mimi', 3, 'ChongQing')
print(dog.localtion) # ==> GuangDong
print(cat.localtion) # ==> ChongQing
print(Animal.localtion) # ==> Asia
可见,在类属性和实例属性同时存在的情况下,实例属性的优先级是要高于类属性的,在操作实例的时候,优先是操作实例的属性。
那通过实例,可不可以修改类属性呢?我们来尝试一下:
cat.localtion = 'Africa'
print(Animal.localtion) # ==> Asia
这里依然打印了Asia,可见通过实例是无法修改类的属性的,事实上,通过实例方法修改类属性,只是给实例绑定了一个对应的实例属性:
# 新增的实例属性
print(cat.localtion) # ==> Africa
因此,需要特别注意,尽量不要通过实例来修改类属性,否则很容易引发意想不到的错误。
4. 属性访问限制
并不是所有的属性都可以被外部访问的,这种不能被外部访问的属性称为私有属性。私有属性是以双下划线’_ _'开头的属性。
- 类私有属性
class Animal(object):__localtion = 'Asia'print(Animal.__localtion) # 报错 AttributeError: type object 'Animal' has no attribute '__localtion'
- 实例私有属性
class Animal(object):def __init__(self, name, age, localtion):self.name = nameself.age = ageself.__localtion = localtiondog = Animal('wangwang', 1, 'GuangDong')
print(dog.name) # ==> wangwang
print(dog.age) # ==> 1
print(dog.__localtion) # 报错 AttributeError: 'Animal' object has no attribute '__localtion'
在外部访问私有属性将会抛出异常,提示没有这个属性。虽然私有属性无法从外部访问,但是,从类的内部是可以访问的。私有属性是为了保护类或实例属性不被外部污染而设计的。
5. 定义实例方法
前面提到,私有属性没有办法从外部访问,只能在类的内部操作;那如果外部需要操作私有属性怎么办?这个时候可以通过定义类或者实例的方法来操作私有属性。
实例的方法指的就是在类中定义的函数,实例方法的第一个参数永远都是self,self是一个引用,指向调用该方法的实例对象本身,除此以外,其他参数和普通函数是完全一样的。
class Person(object):def __init__(self, name):self.__name = namedef get_name(self):return self.__name
在上面的定义,name是实例的私有属性,从外部是无法访问的,而get_name(self) 就是一个实例方法,在实例方法里面是可以操作私有属性的,注意,它的第一个参数是self。
另外,init(self, name)其实也可看做是一个特殊的实例方法。通过定义get_name(self)方法,在外部就可以通过这个方法访问私有属性了。
p = Person('Alice')
print(p.get_name()) # ==> Alice
6. 定义类方法
为了操作实例对象的私有属性,我们定义了实例方法;同样的,如果需要需要操作类的私有属性,则应该定义类的方法。默认的,在class中定义的全部是实例方法,实例方法第一个参数 self 是实例本身。
要在class中定义类方法,需要这么写:
class Animal(object):__localtion = 'Asia'def __init__(self, name, age):self.name = nameself.age = age@classmethoddef set_localtion(cls, localtion):cls.__localtion = localtion@classmethoddef get_localtion(cls):return cls.__localtionprint(Animal.get_localtion()) # ==> Asia
Animal.set_localtion('Afica')
print(Animal.get_localtion()) # ==> Africa
和实例方法不同的是,这里有两点需要特别注意:
- 类方法需要使用@classmethod来标记为类方法,否则定义的还是实例方法
- 类方法的第一个参数将传入类本身,通常将参数名命名为 cls,上面的 cls.__localtion 实际上相当于Animal.__localtion。
六、类的继承
1. 继承类
对人类的抽象可以定义为Person类,而学生、老师等,也都是人类,所以,在Python当中,如果定义学生Student的类,可以继承Person类。
class Person(object):def __init__(self, name, gender):self.name = nameself.gender = gender
接着定义Student类,在定义Student类的时候,由于继承了Person类,所以Student类自动拥有name、gender属性,因此,在定义Student类的时候,只需要把额外的属性加上即可。
class Student(Person):def __init__(self, name, gender, score):super(Student, self).__init__(name, gender)self.score = scorestudent = Student('Alice', 'girl', 100)
print(student.name) # ==> Alice
print(student.gender) # ==> girl
print(student.score) # ==> 100
在定义继承类的时候,有两点是需要【注意】的:
- class Student()定义的时候,需要在括号内写明继承的类Person
- 在__init__()方法,需要调用super(Student, self).init(name, gender),来初始化从父类继承过来的属性
2.判断类型
随着我们学习步伐的前进,我们的程序会出现越来越多的类型,有我们自己定义的类,也有Python自有的str、list、dict等,他们的本质都是都是Python中的一种数据类型,这时有必要去判断数据的类型,通过函数isinstance()可以判断一个变量的类型。
class Person(object):def __init__(self, name, gender):self.name = nameself.gender = genderclass Student(Person):def __init__(self, name, gender, score):super(Student, self).__init__(name, gender)self.score = scoreclass Teacher(Person):def __init__(self, name, gender, course):super(Teacher, self).__init__(name, gender)self.course = coursep = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')
print(isinstance(p, Person)) # True,p是Person类型
print(isinstance(p, Student)) # False,p不是Student类型
print(isinstance(p, Teacher)) # False,p不是Teacher类型print(isinstance(s, Person)) #True,s是Person类型
print(isinstance(s, Student)) #True,s是Student类型
print(isinstance(s, Teacher)) #False,s不是Teacher类型
s 是Student类型,不是Teacher类型,这很容易理解。但是,s 也是Person类型,因为Student继承自Person,虽然它比Person多了一些属性和方法,但是,把 s 看成Person的实例也是可以的。
这说明在一条继承链上,一个实例可以看成它本身的类型,也可以看成它父类的类型。
isinstance也可以用于Python自有数据类型的判断。
s = 'this is a string.'
n = 10
print(isinstance(s, str)) # ==> True
print(isinstance(n, str)) # ==> False
3.多态
类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Person 派生出 Student和Teacher ,并都写了一个who() 方法:
class Person(object):def __init__(self, name, gender):self.name = nameself.gender = genderdef who(self):return 'I am a Person, my name is %s' % self.nameclass Student(Person):def __init__(self, name, gender, score):super(Student, self).__init__(name, gender)self.score = scoredef who(self):return 'I am a Student, my name is %s' % self.nameclass Teacher(Person):def __init__(self, name, gender, course):super(Teacher, self).__init__(name, gender)self.course = coursedef who(self):return 'I am a Teacher, my name is %s' % self.name
class Boss(Person):def __init__(self, name, gender,company):super(Boss, self).__init__(name, gender)self.company = companyp = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')
b = Boss('Bob', 'Male', 'Alibaba')
print(p.who()) # I am a Person, my name is Tim
print(s.who()) # I am a Student, my name is Bob
print(t.who()) # I am a Teacher, my name is Alice
print(b.who()) # ==> I am a Person, my name is Bob
这种行为称为多态。从定义上来讲,Student和Teacher都拥有来自父类Person继承的who()方法,以及自己定义的who()方法。但是在实际调用的时候,会首先查找自身的定义,如果自身有定义,则优先使用自己定义的函数;如果没有定义,则顺着继承链向上找。
七、类的特殊方法
1. 类的__str__方法
对于Python的内建对象,比如int、dict、list等,通过str()方法,可以把这些对象转换为字符串对象输出。
num = 12
str(num) # ==> '12'
d = {1: 1, 2: 2}
str(d) # ==> '{1: 1, 2: 2}'
l = [1,2,3,4,5]
str(l) # ==> '[1, 2, 3, 4, 5]'
2. 类的__slots__方法
由于Python是动态语言,任何实例在运行期都可以动态地添加属性。比如:
class Student(object):def __init__(self, name, gender, score):self.name = nameself.gender = genderself.score = score
此时,Student类有三个属性,name、gender、score,由于是动态语言,在运行时,可以随意添加属性。
student = Student('Bob', 'Male', 99)
student.age = 12 # ==> 动态添加年龄age属性
如果要限制添加的属性,例如,Student类只允许添加 name、gender和score 这3个属性,就可以利用Python的一个特殊的__slots__来实现。
class Student(object):__slots__ = ('name', 'gender', 'score')def __init__(self, name, gender, score):self.name = nameself.gender = genderself.score = score
student = Student('Bob', 'Male', 99)
student.age = 12 # AttributeError: 'Student' object has no attribute 'age'
3. 类的其他方法
在Python中,类似于__str__方法的特殊方法还有很多。这些特殊方法通常被称为“魔法方法”或“双下划线方法”。比如:
- repr:返回一个对象的“官方”字符串表示形式。与__str__不同,__repr__的返回值应该是一个精确的、无歧义的字符串表示形式,用于调试和开发。
- eq:定义对象的相等性比较操作(例如obj1 == obj2)。该方法应返回一个布尔值,表示两个对象是否相等。
- iter:使对象成为可迭代的。该方法应返回一个迭代器对象,用于遍历对象的元素。
- getitem:使得对象支持下标操作(例如obj[index])。该方法用于获取指定索引处的元素。
- setitem:使得对象支持下标赋值操作(例如obj[index] = value)。该方法用于设置指定索引处的元素值。
- delitem:使得对象支持下标删除操作(例如del obj[index])。该方法用于删除指定索引处的元素。
八、模块
1. 创建模块
Python语言本身提供了非常多的模块,比如数学模块math、cmath、decimal、statistics;文件模块pathlib、stat、shutil等;除了使用官方模块,有时候也需要自定义模块。
如果我们需要创建一个tools模块,用来实现众多的工具函数,那么我们可以创建一个tools.py的文件,并在这个文件里面实现一些函数,如:say_hello()函数、say_goodbye()函数。
# tools.py
def say_hello():print('hello')def say_goodbye():print('goodbye')
2. 导入模块
(1)导入官方模块
Python使用import语句导入一个模块,导入官方模块,不需要考虑路径的问题,例如,导入系统自带的模块 math,直接导入即可。
import math
导入以后,你就可以认为math是一个指向已导入模块的变量,通过该变量,我们可以访问math模块中所定义的所有公开的函数、变量和类:
# 属性:圆周率
import math
print(math.pi) # 3.141592653589793# 函数:次方
print(math.pow(2, 3)) # 8.0
如果希望导入模块的指定部分属性或函数,那么使用from…import…语句。
# 属性:圆周率
from math import pi
print(pi) # 3.141592653589793
这个时候,由于pow()函数没有导入,所以是不能使用pow()函数的。如果希望导入模块里面的所有内容,那么使用from …import *语句。
from math import *
print(pi) # 3.141592653589793
print(pow(2, 3)) # 8.0
如果从一个模块导入函数,有可能会遇到导入的函数与本文件的函数冲突的情况。例如:本文件定义了一个pow()函数,同时从math模块也导入了一个pow()函数,这种情况下就会引起冲突;事实上,这种冲突的情况经常发生。
有两种方法可以解决这个问题,第一种是直接导入模块,不指定导入模块里面的具体内容;第二种方法就是使用from … import as …语句,as类似重命名,可以把导入的函数或属性重命名为别的名字。
from math import pow as mathpow
print(mathpow(2, 3)) # 8.0
(2)导入自定义模块
在pycharm中,创建了demo.py模块如下:
# demo.py模块
def say_hello():print('say_hello')
假如想要在demo2模块中导入demo模块,但不知道demo在哪个文件夹目录下,此时有2种方法:
- 使用__file__属性:这个属性可以返回模块文件的路径。但是,这个方法并不总是有效,特别是当模块是内置的或者是在某种特殊的环境中加载的时候。
import demo
print(demo.__file__) # E:\myProject\python_project\pythonProject\demo.py
- 使用os模块的path.abspath()函数:可以返回给定文件或目录的绝对路径。
import os
import demo
print(os.path.abspath(demo.__file__)) # E:\myProject\python_project\pythonProject\demo.py
这样,我们得到demo.py模块的路径后,就可以再demo2进行导入使用了:
import sys
sys.path.append('E:\\myProject\\python_project\\pythonProject') # 添加模块demo所在的目录到系统路径中
import demo
demo.say_hello()
九、输入内容
到目前为止,我们编写的程序都是直接运行的,在运行过程中并没有接收程序外部的输入。
如果想向Python程序输入内容,使用input()函数即可接收外部的输入。如:
num = input('please input number: ')
print(num)
print(type(num))
IDLE运行,效果如下:
注意:input()函数中,输入的是字符串,有时候需要转型为数字类型,否则会报错。例如:
num = input('please input number: ')
num = int(num) #不转为整形,下面代码将无法运行
for i in range(1,num):print(i)
十、函数式编程
1. map()
map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f依次作用在list的每个元素上,map()函数会返回一个迭代器,可以依次迭代得到原来list的元素被函数f处理后的结果。
例如,对于list [1, 2, 3, 4, 5, 6, 7, 8, 9]。
如果希望把list的每个元素都作平方,就可以利用map()函数。
我们定义需要传入函数f(x)=x*x,就可以利用map()函数完成这个计算:
def f(x):return x*xfor item in map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]):print(item)
# 结果:[1, 4, 9, 10, 25, 36, 49, 64, 81]
2. reduce()
和map函数一样,reduce()函数也是Python内置的一个高阶函数。reduce()函数接收的参数和 map() 类似,一个函数 f,一个list,但行为和 map()不同,reduce()传入的函数 f 必须接收两个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果值。
例如,编写一个f函数,接收x和y,返回x和y的和:
from functools import reducedef f(x, y):return x + yprint(reduce(f, [1,3,5,7,9])) # ==> 25
得到的结果是25,实际过程是这样的,reduce()函数会做如下计算:
先计算头两个元素:f(1, 3),结果为4;
再把结果和第3个元素计算:f(4, 5),结果为9;
再把结果和第4个元素计算:f(9, 7),结果为16;
再把结果和第5个元素计算:f(16, 9),结果为25;
由于没有更多的元素了,计算结束,返回结果25。
上述计算实际上是对 list 的所有元素求和。虽然Python内置了求和函数sum(),但是,利用reduce()求和也很简单。
reduce()还可以接收第3个可选参数,作为计算的初始值。如果把初始值设为100,计算:
print(reduce(f, [1, 3, 5, 7, 9], 100)) # ==> 125
结果将变为125,因为第一轮计算是:
计算初始值和第一个元素:f(100, 1),结果为101。
3. filter()
filter()函数是 Python 内置的另一个有用的高阶函数,filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,filter()根据判断结果自动过滤掉不符合条件的元素,并返回一个迭代器,可以迭代出所有符合条件的元素。
例如,要从一个list [1, 4, 6, 7, 9, 12, 17]中删除偶数,保留奇数,首先,要编写一个判断奇数的函数:
def is_odd(x):return x % 2 == 1
for item in filter(is_odd, [1, 4, 6, 7, 9, 12, 17]):print(item)
4. sorted()
Python内置的 sorted()函数可对list进行排序:
>>> sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]
可以看到,sorted()函数,默认是由小到大排序列表的元素。
>>> score = [('Alice', 72), ('Candy', 90), ('Bob', 62)]
>>> sorted(score)
[('Alice', 72), ('Bob', 62), ('Candy', 90)]
当list的每一个元素又是一个容器时,则会以第一个元素来排序,比如在score中,每个元素都是包含名字和成绩的一个tuple,sorted()函数则按名字首字母进行了排序并返回。
对于上述排序成绩的情况,默认是按照第一个名字进行排序的,有没有办法让sorted()函数按照成绩来进行排序呢?
如果需要按照成绩高低进行排序,需要指定排序的字段是成绩,sorted接受key参数,用来指定排序的字段,key的值是一个函数,接受待排序列表的元素作为参数,并返回对应需要排序的字段。因此,sorted()函数也是高阶函数。
score = [('Alice', 72), ('Candy', 90), ('Bob', 62)]
def k(item):return item[1] # ==> 按成绩排序,成绩是第二个字段new_socre = sorted(score, key=k)
print(new_socre) # [('Bob', 62), ('Alice', 72), ('Candy', 90)]# 如果需要倒序,指定reverse参数即可
new_socre2 = sorted(score, key=k, reverse=True)
print(new_socre2) # [('Candy', 90), ('Alice', 72), ('Bob', 62)]
5. range()
在 Python 中,range 是一个用于生成一系列整数的函数。它可以接受一个、两个或三个参数,返回一个表示等差整数序列的可迭代对象。
range 的基本语法是:
range(stop)
range(start, stop)
range(start, stop, step)
- start: 序列的起始值,默认为0。
- stop: 序列的结束值,但不包括该值。
- step: 步长(增量),表示连续数字之间的差。默认为1。
range 生成的序列是左闭右开的,即包括起始值,但不包括结束值。
++ 只有一个参数 (stop):
for i in range(5):print(i)
# 结果:
# 0
# 1
# 2
# 3
# 4
++ 两个参数 (start, stop):
for i in range(2, 8):print(i)
# 结果:
# 2
# 3
# 4
# 5
# 6
# 7
++ 三个参数 (start, stop, step):
for i in range(1, 10, 2):print(i)
# 结果:
# 1
# 3
# 5
# 7
# 9
range 还可以用于创建一个列表:
my_list = list(range(5, 20, 3))
print(my_list) # [5, 8, 11, 14, 17]
案例:找出1到20内的所有质数
for num in range(2, 21): # 起始值为2,对于范围在2到20的每一个数字for i in range(2, num): # 对于从2到num-1的每一个数字if num % i == 0: # 如果num能被i整除break # 退出内层循环,说明num不是质数else:print(num) # 如果内层循环完整执行(即未中断),则说明num是质数,打印输出
# 结果:2、3、5、7、11、13、17、19
6. 闭包
在函数内部定义的函数和外部定义的函数是一样的,只是他们无法被外部访问:
def g():print('g()...')def f():print('f()...')return g
f() # ==> f()...
将g的定义移入函数 f 内部,防止其他代码调用 g:
def f():print('f()...')def g():print('g()...')return g
f() # ==> f()...
假如,有这么一个 calc_sum 函数:
def calc_sum(list_):def lazy_sum():return sum(list_)return lazy_sum
注意: 发现没法把 lazy_sum 移到 calc_sum 的外部,因为它引用了 calc_sum 的参数 list_。
像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。
闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。举例如下:
# 希望一次返回3个函数,分别计算1x1,2x2,3x3:
def count():fs = []for i in range(1, 4):def f():return i*ifs.append(f)return fsf1, f2, f3 = count()
print(f1()) #9 【因为f1现在才计算i*i,但现在i的值已经变为3】
print(f2()) #9
print(f3()) #9
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果全部都是 9(请自己动手验证)。
原因就是当count()函数返回了3个函数时,这3个函数所引用的变量 i 的值已经变成了3。由于f1、f2、f3并没有被调用,所以,此时他们并未计算 i*i,当 f1 被调用时,i的值已经变为3。
因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。