(4) 函数、Bug、类与对象、封装、继承、多态、拷贝

目录

函数

函数的创建和调用

函数的参数传递

函数的返回值

函数的参数定义

变量的作用域

递归函数

Bug

Bug常见类型

常见异常类型

异常处理机制

类与对象

类和对象的创建

类属性、类方法、静态方法

动态绑定属性和方法

封装

继承

多继承

方法重写

object类

多态

动态语言与静态语言

特殊属性和特殊方法

类的浅拷贝与深拷贝

浅拷贝

深拷贝


函数

函数就是执行特定任何以完成特定功能的一段代码

优点:复用代码,隐藏实现细节,提高可维护性,提高可读性便于调试

函数的创建和调用

#例子
def addnum(a,b):   #创建
    c=a+b
    return c
result=addnum(10,20)   #调用
print(result) #30

函数的参数传递

1.位置实参:根据形参对应的位置进行实参传递

2.关键字实参:根据形参名称进行实参传递

函数的返回值

函数返回多个值时,结果为元组

1.如果函数没有返回值【函数执行完之后,不需要给调用处提供数据】return可以忽略不写

2.函数的返回值,如果是1个,直接返回类型

3.函数的返回值,如果是多个,返回的结果为元组

函数的参数定义

1.函数定义默认值参数

   函数定义时,给形参设置默认值,只与默认值不符都时候才需要传递实参

def fun(a,b=10):
    print(a,b)
#函数调用
fun(100) #100,10 只传一个参数,b采用默认值
fun(20,30) #20,30 传两个值,30把10替换

2.个数可变的位置参数

    定义函数时,无法事先确定传递的位置实参个数,可用 * 定义个数可变的位置形参 ,结果为一  个元组

def fun(*args):
    print(args)
fun(10) #(10,)
fun(10,20,30) #(10,20,30)

3.个数可变的关键字形参

    定义函数时,无法事先确定传递的关键字实参个数,可用 ** 定义个数可变的关键字形参 ,结果为一 个字典

def fun(**args):
    print(args)
fun(a=10) #{'a':10}
fun(a=10,b=20,c=30) #{'a':10,'b':20,'c':30}

 注:个数可变的位置参数只能是1个 (不能写成fun(*args,*a));

        个数可变的关键字参数只能是1个 (不能写成fun(**args,**a));

         在一个函数的定义过程中,可以同时有个数可变的位置参数和个数可变的关键字参数,但是个数可变的位置形参,要放在个数可变的关键字形参的前面。

4.函数参数总结 :

变量的作用域

程序代码能访问该变量的区域
根据变量的有效范围可分为:
        1.局部变量
        •在函数内定义并使用的变量,只在函数内部有效,局部变量使用global声明,这个变量
        就会就成全局变量
        2.全局变量
        •函数体外定义的变量,可作用于函数内外

递归函数

如果在一个函数的函数体内调用了该函数本身,这个函数就称为递归函数
(1) 递归的组成部分:
        递归调用与递归终止条件
(2) 递归的调用过程:
        1.每递归调用一次函数,都会在栈内存分配一个栈帧,
        2.每执行完一次函数,都会释放相应的空间
(3) 递归的优缺点:
        缺点:占用内存多,效率低下
        优点:思路和代码简单

Bug

Bug常见类型

常见异常类型

异常处理机制

#举例try...except...else
#如果try块中没有抛出异常,则执行else块;如果try中抛出异常,则执行except块
try:
    n1=int(input('输入一个整数:'))
    n2=int(input('输入另一个整数:'))
    result=n1/n2
except BaseException as e:
    print('error')
    print(e)
else:
    print('结果为',result)


#try...except...else...finally结构
#finally块无论是否发生异常都会被执行,能常用来释放try块中申请的资源

类与对象

 编程分两种思维:面向过程、面向对象

(面向过程,事物简单,线性思维解决;面向对象,事物复杂,线性思维无法解决;但是两者并不冲突,是相辅相成的,面向对象便于从宏观上把握事物间的复杂关系,然后用面向过程方式具体处理)

类是多个类似事物组成的群体的统称。

(不同的数据类型属于不同类,使用内置函数type()查看)

对象是在类之下包含的相似的不同个例,这个个例被称为实例或对象

(例如100、33、56都是int类下包含的相似的不同个例。python中一切皆对象)

类和对象的创建

(1)类的创建

 

class Student:  #Student为类的名称(类名)由一个或多个单词组成,每个单词首字母大写,其余小写
    native_place='四川' #直接写在类里的变量,称为类属性
    def __init__(self,name,age): #__init__(self)初始化,可以添加属性;name, age为实例属性
        self.name=name  #self.name称为实例属性,进行了一个赋值,将局部变量的值赋给实体属性
        self.age=age
#实例方法
    def info(self):
        print('我的名字叫:',self.name,'年龄是:',self.age)
#类方法
    @classmethod
    def cm(cls):
        print ('类方法')
#静态方法
    @staticmethod
    def sm():
        print('静态方法')

#在类中定义的称为方法,在类外定义的称为函数
#函数
def fun():
    print('python')

 (2)对象的创建

对象的创建又称为类的实例化,有了实例就可以调用类中的内容

(一个Student类可以创建N多个Student类的实例对象,每个实例对象的属性值不同)

#创建Student类的实例对象
stu=Student('json',20)
print(stu.name) #实例属性
print(stu.age) #实例属性
stu.info() #实例方法  对象名.方法名()
Student.info(stu) #类名.方法名(类的对象)

 

类属性、类方法、静态方法

(1) 类属性:类中方法外的变量称为类属性,被该类的所有对象所共享print(Student.native_place)

(2) 类方法:使用@classmethod修饰的方法,使用类名直接访问的方法Student.cm()

(3) 静态方法:使用@staticmethod修饰的方法,使用类名直接访问的方法Student.sm()

动态绑定属性和方法

 Python是动态语言,在创建对象之后,可以动态地绑定属性和方法

def show():
print('我是一函数,定义在类之外的称函数’)

stu=Student('Jack',20)
stu.gender=’男’#动态绑定属性
print(stu.name,stu.age,stu.gender) #Jack 20 男
stu.show=show #动态绑定方法
stu. show()

封装

面向对象的三大特征:

(1)封装:提高程序的安全性
        •将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在
         类对象的外部调用方法。这样无需关心方法内部的具体实现细节,从而隔离了复
         杂度。
       •在Python中没有专门的修饰符用于属性的私有,如果该属性不希望在类对象外部被
        访问,前边使用两个“_”下划线。
(2)继承:提高代码的复用性
(3)多态:提高程序的可扩展性和可维护性 

#封装
class Student:
    def __init__(self,name,age):
        self.name=name
        self.__age=age #年龄不希望在类的外部被使用,所以加了两个_
    def show(self):
        print(self.name,self.__age)

stu = Student('张三',20)
stu.show()  #张三 20,调用了show()方法,show()输出了name和age,所有在内部两者的皆可访问

#在类外访问name与age
print(stu.name) #张三,可访问
# print(stu.__age) #报错,不可访问
#print(dir(stu)) 利用dir可获得stu中所有属性,其中包含了一个_Student__age属性
#print(stu._Student__age) #20,在类的外部,可以通过_Student__age进行访问

继承

1.如果一个类没有继承任何类,则默认继承object

2.python支持多继承

3.定义子类时,必须在子类构造函数中调用父类的构造函数

#继承
class Person(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def info (self):
        print('姓名:{0},年龄:{1}'.format(self.name,self.age))
#定义子类
class Student(Person):
    def __init__(self,name,age,score):
        super().__init__(name, age)
        self.score=score
#测试
stu=Student('Jack',20,'1001')
stu.info()

#结果   姓名:Jack,年龄:20

多继承

方法重写

1.如果子类对继承父类的某个属性或方法不满意,可以在子类中对其(方法体)进行重新编写

2.子类重写后的方法中,可以使用super().xxx()调用父类中被重写的方法

#方法重写
class Person(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def info (self):
        print('姓名:{0},年龄:{1}'.format(self.name,self.age))
#定义子类
class Student(Person):
    def __init__(self,name,age,score):
        super().__init__(name, age)
        self.score=score
    def info (self):
        super().info()  #这是为了调用父类的方法(不用这条语句,就不会输出name和age)
        print('学号:{0}'.format(self.score)) #这里直接输出了Person类的score
#测试
stu=Student('Jack',20,'1001') #传递实参
stu.info() #调用了Person类里的info()

object类

 1.object类是所有类的父类,因此所有类都有object类的属性和方法

2.内置函数dir()可以查看指定对象所有属性

3.object有个__str__()方法,用于返回一个对于“对象的描述”,对应于内置函数str()经常用于print()  方法,帮助我们查看对象的信息,所有我们经常会对__str__()进行重写

class Person(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def info(self):
        print('姓名:{0},年龄:{1}'.format(self.name,self.age))
#重写__str__()
    def __str__ (self): 
        return '姓名:{0},年龄:{1}'.format(self.name,self.age)

p=Person('Jack',20)
print(dir(p)) #查看对象p中的所有属性
print(p) #默认调用__str__()这样的方法


多态

多态就是“具有多种形态”,它指的是:即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用方法,在运行过程中根据变量所引用对象的类型,动态决定调用哪个对象中的方法。 

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(animal):
    animal.eat ()
fun(Dog())
fun(Cat())
fun(Person())

#Dog、Cat继承于Animal,而Person继承于object,在类外写了一个函数fun(),调用fun()时不仅输出了“狗吃肉;猫吃鱼”,还输出了“人吃五谷杂粮”,这两类没有继承关系,但是还是可以输出“人吃五谷杂粮”。因为,传入一个对象,只要那个对象里含有eat()这个方法,它都会被调用

动态语言与静态语言

 静态语言和动态语言关于多态的区别:
1. 静态语言实现多态的三个必要条件:继承、方法重写、父类引用指向子类对象。
2. 动态语言的多态崇尚“鸭子类型”当看到一只鸟走起来像鸭子、游泳起来像鸭子、收起来也像鸭    子,那么这只鸟就可以被称为鸭子。在鸭子类型中,不需要关心对象是什么类型,到底是不是鸭子,只关心对象的行为(方法的功能)。

特殊属性和特殊方法

#特殊属性
class A:
    pass
class B:
    pass
class C(A,B):
    def __init__(self,name,age):
        self.name=name
        self.age=age
class D(A):
    pass
#创建C类对象
x=C('Jack',20) #x是C类型的一个实例对象
print(x.__dict__) #实例对象的字典属性
print(C.__dict__)
print('------------------------')
print(x.__class__) #输出对象所属的类,属于<class '__main__.C'>
print(C.__bases__) #C类的父类类型元素,加s就输出全部父类,不加s就只输出一个
print(C.__mro__) #类的层次结构
print(A.__subclasses__()) #子类的列表

上述代码的结果为

#特殊方法
#例如
a=10
b=20
c=10+20 #两个整数类型的对象相加,实际上的情况是c=a.__add__(b),a调用了__add__()方法与b相加
------------------------------------------------------------------------------------------
class Student:
    def __init__(self,name):
        self.name=name
    def __add__(self, other): #重写__add__(),如果没有这一步,stu1不能与stu2相加
        return self.name+other.name
    def __len__(self,name):
        return len(self.name)
stu1=Student('Jack')
stu2=Student('Lisa')

s=stu1+stu2 #实现了两个对象的加法运算(因为在Student类中编写__add__()特殊方法)
print(s)
print('-----------------------------------')
lst=[1,2,3,4]
print(len(lst)) #len()是内置函数,用来计算列表长度
print(lst.__len__()) #这个跟上面这条语句效果相同
print(len(stu1.name)) #尝试输出一个对象stu1的长度,报错,因为没有len(),如果想获得这个长度,可以重写__len__()

以上代码运行结果为

类的浅拷贝与深拷贝

变量的赋值操作:只是形成两个变量,实际上还是指向同一个对象

class CPU:
    pass
class Disk:
    pass
class Computer:
    def __init__(self,cpu,disk):
        self.cpu=cpu
        self.disk=disk

#(1)变量的赋值
cpu1=CPU()
cpu2=cpu1
print(cpu1,id(cpu1))
print(cpu2,id(cpu2))

#运行结果为
<__main__.CPU object at 0x00000165C193C1C0> 1536551010752
<__main__.CPU object at 0x00000165C193C1C0> 1536551010752
#两者id相同,它们指向同一个实例对象

 

浅拷贝

python拷贝一般都是浅拷贝,拷贝时,对象包含的子对象内容不拷贝

(源对象与拷贝对象会引用同一个子对象 )

#(2)类有浅拷贝
disk=Disk() #创建一个硬盘类的对象
computer=Computer(cpu1,disk) #创建一个计算机类的对象
print(disk)

#浅拷贝
import copy
computer2=copy.copy(computer)
print(computer,computer.cpu,computer.disk)
print(computer2,computer2.cpu,computer2.disk)


输出的结果为 (前面两条是cpu1和cpu2的id)

深拷贝

使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象

(源对象和拷贝对象所有的子对象也不相同 )

#深拷贝
import copy
computer3=copy.deepcopy(computer)
print(computer,computer.cpu,computer.disk)
print(computer3,computer3.cpu,computer3.disk)

输出结果为

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值