Python面向对象
函数
函数的调用和创建
-
函数的创建:
def calc(): c = 1+2 print(c)
-
函数的调用
result = calc(3,6) print(result)
函数的参数调用
-
位置实参
-
关键字实参
-
调用参数时,若传递值,则函数内的改变不影响原参数值;若传递列表,则追加操纵会改变原列表的值
#可用以下代码验证 def fun(arg1, arg2): print (' arg1=', arg1) print (' arg2=' , arg2) arg1=100 arg2[1] = 55 print (' arg1=', arg1) print(' arg2=', arg2) n1=11 n2=[22,33,44] print(n1) print(n2) print('------') fun(n1, n2) print(n1) #11 print(n2) #[22, 55, 44]
函数的默认参数
-
函数定义时,可以给形参设置默认值,只有与默认值不符的时候才需要传递实参
个数可变的参数
-
个数可变的位置参数
定义函数时,可能无法事先确定传递的位置实参的个数时,使用可变的位置参数
使用*定义个数可变的位置形参
结果为一个元组
def func(*abc): #可变的位置参数 print(abc) func(10) #(10,) func(10,20,30) #(10,20,30)
-
个数可变的关键字形参
定义函数时,无法事先确定传递的关键字实参的个数时,使用可变的关键字形参
使用**定义个数可变的关键字形参
结果为一个字典
def func1(**args): #个数可变的关键字形参 print(args) func1(age=10) #{'age':10} func1(age=10,name='james') #{'age':10,'name':'james'}
变量的作用域
-
局部变量
在函数内定义并使用的变量,只在函数内部有效,局部变量使用global严明,这个变量就会就成全局变量
-
全局变量
函数体外定义的变量,可作用于函数内外
函数的返回值
-
函数返回多个值时,结果为元组
def fun(num): odd=[]#存奇数 even=[]#存偶数 for i in num: if i%2: odd.append(i) else: even.append(i) return odd, even lst = [10,29,34,23,44,53,55] print (fun(lst)) #([29, 23, 53, 55], [10, 34, 44])
递归函数
-
什么是递归函数
如果在一个函数的函数体内调用了该函数本身,这个函数就称为递归函数
-
递归的调用过程
每递归调用一次函数,都会在栈内存分配一个栈帧
每执行完一次函数,都会释放相应的空间
#计算阶乘 def func(n): #n=4--n=3--n=2--n=1 if n == 1: return 1 else: result = n * func(n-1) return result print(func(4))
类
类的创建
-
类的组成:类属性、实例方法、静态方法、类方法
class Student: #类属性 name = 'James' age = 18 #实例方法 def eat(self): print('James能干3碗饭') def calc(self): #self固定写法,不要问为什么 print(1+2) def __init__(self,name1,age1): #用于生成和初始化类属性,name、age1在这里已经是属性了 self.name1 = name1 #self.name 实体属性,赋给实体属性 self.age1 = age1 #静态方法 @staticmethod def method(): #无法访问类或实例属性 print('我使用staticmethod对方法进行了修饰, 所以我是静态方法') #类方法 @classmethod def classmethod(cls):#cls表示类本身 #可以访问类属性 print('我是类方法, 因为我加@classmethod进行修改')
对象的创建
-
对象的创建又称为类的实例化
-
语法:实例名=类名()
#创建出来的对象 stu1 = Student('李四', 20) #参数会复制给类的实例方法中__init__(self,name1,age1)方法 print(stu1) #打印类的地址
-
类属性和实例方法的调用
stu1.eat() stu1.calc() print(stu1.name1) print(stu1.age1)
类属性、类方法、静态方法
-
类属性:类中方法外的变量称为类属性,被该类的所有对象所共享
-
类方法:使用@classmethod修饰的方法,使用类名直接访问的方法
-
静态方法:使用@staticmethod修饰的主法,使用类名直接访问的方法
print(Student.native_place)#访问类属性 Student.sm() #调用静态方法 Student.cm() #调用类方法
- 实例方法和类级别的属性、方法、静态方法的区别是:使用类级别的属性、方法、静态方法不需要创建对象即可调用,使用实例方法必须要创建对象。
- 类属性和对象属性是一个关系是:共性和个性的关系
- 静态方法和类方法的区别是:静态方法不可以访问类或静态属性,类方法可以访问类属性
对象可以动态创建属性和方法
-
Python是动态语言,在创建对象之后,可以为某对象动态地创建属性和方法。但是该属性和方法是该对象专属,而不是创建给类的。这个功能体现了对象的定制化。
#定义一个类--学生类 class Student: #定义吃饭这个行为 #实例方法 def eat(self): print('James能干3碗饭') def __init__(self,name,age): self.name = name #self.name 实体属性,赋给实体属性 self.age = age stu1 = Student('James', 30) stu2 = Student('Kate', 26) print('----stu1对象动态绑定属性-------') stu1.address = 'changsha' #类没有该属性,但可为该对象动态添加专属属性 print(stu1.name, stu1.age,stu1.address) #print(stu2.name, stu2.age,stu2.address) 虽然创建了属性,但没有给stu2创建,stu2没有address,会报错 print('----stu2对象动态绑定方法-------') def playBall(): print('定义在类的外面,叫函数') stu2.playBall = playBall stu2.playBall() #给stu2创建了专属对象方法 #stu1.playBall() #报错,stu1没有拓展该方法
浅拷贝和深拷贝
变量的赋值
-
只是形成两个变量,实际上还是指向同一个对象
class Father: pass class Mother: pass class Family: def __init__(self,father, mother): self.father = father self.mother = mother #变量的赋值 fa1 = Father() fa2 = fa1 #赋值操作使f1和f2指向相同的地址块,当其中某个属性或方法改变时,才创建新的地址块 print(fa1, fa2)
浅拷贝
-
Python拷贝一般都是浅拷贝,拷贝时,对象包含的子对象内容不拷贝,因此,源对象与拷贝对象会引用同一个子对象。
-
也即:拷贝对象和被拷贝的对象是同一块空间。只有其中一个改变时,才会开辟新的空间
fa1 = Father() mo1 = Mother() family = Family(fa1,mo1) #创建一个家庭对象, 家庭对象的属性有真实存在的爸 妈对象 #浅拷贝 import copy family2 = copy.copy(family) #family和fanily2是同一个对象,father和mother也是一样的 print(family, family.father, family.mother) print(family2, family2.father, family2.mother)
深拷贝
-
使用copy模块的deepcopv函数﹒递归地对对象和对象的所有子对象进行拷贝。
-
也即:拷贝对象和被拷贝对象的对象不是同一块地址空间,在深拷贝时,就创建了一块新的地址用于存放拷贝对象
#深拷贝 import copy family3 = copy.deepcopy(family) print(family, family.father, family.mother) print(family3, family3.father, family3.mother)
面向对象的三大特征
封装
-
封装:提高程序的安全性
-
在Python中没有专门的修饰符用于属性的私有,如果该属性不希望在类对象外部被访问,前边使用两个“_”。
#定义一个类--学生类 class Person: def __init__(self,name,age): self.name = name #self.name 实体属性,赋给实体属性 self.__age = age #私有属性 def study(self): print('开始学习了, James今年'+str(self.__age)+'岁') james = Person('James', 30) james.study() #__age私有属性封装在study()内部,保护了数据的安全 print(james.name) #可以通过对象直接访问公共属性 #print(james.__age) #但不可以通过对象访问私有属性 #那么如何访问呢? #在类的外部使用 __age print(dir(james)) #['_Person__age',…… print(james._Person__age) #通过这个方式可以访问
继承
继承的定义
-
继承:提高代码的复用性
-
如果一个类没有继承任何类,则默认继承object
-
定义子类时,必须在其构造函数中调用父类的构造函数
-
语法格式:
class 子类类名(父类1,父类2…) :
pass
class Person(object): #默认object def __init__(self, name, age): self.name = name self.age = age def info(self): print(self.name, self.age) #定义子类时,必须在其构造函数中调用父类的构造函数 class Student(Person): def __init__(self, name, age, stu_no): super().__init__(name, age) #super表示直接调用其父类__init__ self.stu_no = stu_no class Teacher(Person): def __init__(self, name, age, address): super().__init__(name, age) #super表示直接调用其父类__init__ self.address = address stu = Student('James', 30, '999') stu.info() teacher = Teacher('Kate', 34, 'changsha') teacher.info()
-
多继承
-
Python支持多继承
#多继承 class A(object): pass class B(object): pass class C(A, B): pass
方法重写
-
如果子类对继承自父类的某个属性或方法不满意,可以在子类中对其(方法体)进行重新编写
-
子类重写后的方法中可以通过super().xxx()调用父类中被重写的方法
class Person(object): #默认object def __init__(self, name, age): self.name = name self.age = age def info(self): print(self.name, self.age) class Student(Person): def __init__(self, name, age, stu_no): super().__init__(name, age) #super表示直接调用其父类__init__ self.stu_no = stu_no #父类的info方法满足不了子类, 子类按自己的需求对info二次重写 def info(self): super().info() print(self.stu_no) class Teacher(Person): def __init__(self, name, age, address): super().__init__(name, age) #super表示直接调用其父类__init__ self.address = address stu = Student('James', 30, '999') stu.info()
类的祖宗object类
-
object类是所有类的父类,因此所有类都有object类的属性和方法。
-
内置函数dir()可以查看指定对象所有属性
-
Object有一个__str__()方法,用于返回对象的地址
class Student: def __init__(self, name ,age): self.name = name self.age = age def __str__(self): #重写object父亲__str__ return '我的名字叫{0}, 我的年纪{1}'.format(self.name,self.age) stu = Student('james', 18) print(dir(stu)) #可以查看指定对象的所有属性 print(stu) #打印对象时,默认调用对象的__str__()函数,打印对象地址。重写__str__()后,打印对象时,打印重写内容。'我的名字叫{0}……'
多态
多态的定义
-
多态:提高程序的可扩展性和可维护性
-
简单地说,多态就是“具有多种形态",它指的是:即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用方法,在运行过程中根据变量所引用对象的类型,动态决定调用哪个对象中的方法。
class Animal(object): def eat(self): print('动物会吃东西') class Dog(Animal): def eat(self): print('狗吃肉') class Cat(Animal): def eat(self): print('猫吃老鼠') class Person(object): def eat(self): print('人吃饭') def fun(obj): obj.eat() fun(Cat()) #省略了cat=Cat(),的变量定义操作,传入了Cat(),则默认使用Cat()中的eat方法 fun(Dog()) fun(Person())
个人理解,多态是重写的应用
静态语言与静态语言
- 静态语言实现多态的三个必要条件
- 继承
- 方法重写
- 父类引用指向子类对象
- 动态语言的多态崇尚“鸭子类型"当看到一只鸟走起来像鸭子、游泳起来像鸭子、收起来也像鸭子,那么这只鸟就可以被称为鸭子。在鸭子类型中,不需要关心对象是什么类型,到底是不是鸭子,只关心对象的行为。
Python的特殊属性与特殊方法(了解)
-
__dict__、__len__()、__add__()、__new__()、___init__()
-
__dict__
class A: pass class B: pass class C(A,B): def __init__(self, name , age): self.name = name self.age = age obj = C('james', 18) print(obj.__dict__) #输出obj对象有哪些属性参数 print(C.__dict__) print(obj.__class__) #obj由C类创建的 print(C.__bases__) #C类所有的父类 print(C.__base__) #打印离C最近的父类 print(C.__mro__) #打印C类所有的继承关系
-
__add__()、__len__()
#先讲__add__() a = 1 b = 2 c = a+b #a+b实际上调用的是a.__add__(b) cc = a.__add__(b) print(c) print(cc)#c与cc相同 class Teacher: def __init__(self, name): self.name = name def __add__(self, other):#重写__add__()方法 return self.name+other.name def __len__(self):#重写__len__()方法 return len(self.name) t1 = Teacher('James') t2 = Teacher('Kate') s = t1+t2 #t1和t2指向的是对象的内存地址,地址相加没有意义,我们的目的是让两个对象的字符串相加,那么我们就需要通过重写object类的__add__()方法达到此效果 ss = t1.__add__(t2) print(s) print(ss) #再讲__len__() lst = [1,2,3,4,5] print(len(lst)) #len()实际上是调用__len__()方法 print(lst.__len__()) print(len(t1)) #对于t1这个对象,无法使用len(),则可以通过重写__len__()方法,让内置函数的len()方法的参数可以是任意类型
-
__init__()
class Student(object): def __new__(cls, *args, **kwargs): #__new__()该方法下面的过程会在父类Object中自动重写 print('__new__方法被调用,cls的id值{0}'.format(id(cls))) #相当于类对象Student obj = super().__new__(cls) #创建一个真实的对象 obj = Student() print('obj 对象被创建出来后, id值{0}'.format(id(obj))) #相当于真实对象 return obj def __init__(self, name, age): #这里self的作用就是把obj传进来,所以id和obj是一样的 print('__init__被调用, self对象的id值{0}'.format(id(self))) self.name = name self.age = age obj = Student('James', 18) print('obj对象的id值{0}'.format(id(obj)))
__new__()讲解:
- 是静态方法,第一个参数必须是cls
- 所有对象实例的诞生都必须经过__new__()创建对象–>__init__()初始化对象
- 重写__new__()之后必须调用super.__new__()
模块
模块的定义
- 一个模块中可以包含N多个函数。在Python中一个扩展名为.py的文件就是一个模块。
模块的好处
- 方便其它程序和脚本的导入并使用
- 避免函数名和变量名冲突
- 提高代码的可维护性
- 提高代码的可重用性
模块的导入
-
创建模块
新建一个.py文件,名称尽量不要与Python自带的标准模块名称相同导入模块 -
导入模块
- import 模块名称 [as别名]
- from 模块名称 import 函数/变量/类
#导入自带模块 #import math from math import pi,pow,ceil,floor print(pi)
#自定义模块calcDemo.py def add(a,b): return a+b def reduce(a,b): return a-b
#导入自定义模块calcTestModel import calcDemo print(calcDemo.add(1,4)) print(calcDemo.reduce(6,2))''' from calcDemo import add,reduce print(add(1,4)) print(reduce(6,2))
如何以主程序模式运行模块
-
在每个模块的定义中都包括一个记录模块名称的变量__name__,程序可以检查该变量,以确定他们在哪个模块中执行。
-
如果一个模块不是被导入到其它程序中执行,那么它可能在解释器的顶级模块中执行。
-
顶级模块的__name__变量的值为__main__
if __name__= '__main__' : pass
-
举例说明
#自定义模块calcdemo.py def add(a,b): return a+b print(add(3,6))
#引用模块 import calcdemo print(calcdemo.add(7,9)) #会打印9和16,我们至引用了add,不应该执行print(add(3,6))
在自定义模块中加上一行
#自定义模块calcdemo.py def add(a,b): return a+b if __name__ == '__main__': #加上这个以后表示这行代码的执行仅在该文件执行时执行 print(add(3,6))
python中的包
什么是包
- 包是一个分层次的目录结构,它将一组功能相近的模块组织在一个目录下
包的作用
- 代码规范
- 避免模块名称冲突
包与目录的区别
- 包含_init__.py文件的目录称为包
- 目录里通常不包含__init__py文件
包的导入
- import 包名、模块名
包的创建与引用
-
右键new选择python package,给文件夹明明packagedemo,创建包
-
包下放置不同的模块model1.py
-
如图
-
项目如果想引用该模块,只需要
import packagedemo.model1 print(packagedemo.model1.a) #为了方便使用,可用以下方式 import packagedemo.model1 as mu print(mu.a) #或者 from packagedemo import model1 print(model1 .a) #或者 from packagedemo.model1 import a print(a)
python中常用的内置模块
-
如下
#选几个重要的讲一下 import sys print(sys.getsizeof(34)) #28字节,python真费内存 print(sys.getsizeof(77)) print(sys.getsizeof(True)) print(sys.getsizeof(False)) import time print(time.time()) #非格式化的时间 print(time.localtime(time.time())) #格式化 import urllib.request print(urllib.request.urlopen('http://www.baidu.com').read()) #相当于在浏览器访问网页后用read读取html from math import pi,pow,ceil,floor print(dir(math)) #查看math内有哪些方法、参数可以用 print(pi) print(pow(2,4)) #2的4次方 print(ceil(9.001)) #向上取整,10 print(floor(9.8)) #向下取整,9
python使用第三方模块
安装第三方模块
-
在windows命令行工具中输入:
pip install schedule #以此模块为例
-
在pychram下方有个terminal窗口,相当于命令行工具,同样输入
pip install schedule
验证是否安装成功
-
terminal中输入python,尝试引入
import schedule
使用模块
-
以schedule为例
import schedule import time def job(): print('每隔3S打印一下') schedule.every(3).seconds.do(job) #每隔3S来执行一下job函数,加入计划 while True: schedule.run_pending() #执行计划 time.sleep(2) #计划未启动成功则休眠2s