python入门详解 3

本文章内容:

  1. 函数,可变参数,作用域,递归函数,
  2. bug 分析,try…except…结构, try…except…else结构,try…except…else…finally结构,常见异常类型
  3. 类,动态绑定属性和方法
  4. 面向对象–封装,继承,方法重写,object类,多态, 特殊方法和特殊属性,类的浅拷贝与深拷贝
# author: ljt
# 2023/6/11 14:33

'''
在函数调用过程中,进行参数的传递
如果是不可变对象,在函数体的修改不会影响实参的值
如果是可变对象,在函数体的修改会影响到实参的值
'''


# 个数可变的位置参数
# 1. 定义函数时,可能无法事先确定传递的位置实参的个数时,使用可变的位置形参
# 2. 使用 * 定义个数可变的位置形参
# 3. 结果为一个元组
# 个数可变的关键字形参
# 1. 定义函数时,无法事先确定传递的关键字实参的个数时,使用可变的关键字形参
# 2. 使用 ** 定义个数可变的关键字形参
# 3. 结果为一个字典

def fun(*args):
    print(args)

def show(**args):
    print(args)

# 会报错,个数可变的位置参数,只能定义一个
# def fun1(*args, *a):
#     print(args)

# 会报错,个数可变的关键字参数,只能定义一个
# def show1(**args, **啊):
#     print(args)

# 报错,在一个函数定义中,既有个数可变的关键字形参也有个数可变的位置形参,要求 个数可变的位置形参在前
# def f1(**args, *a):
#     print(args, a)

def f(*args, **a):
    print(args, a)

fun(10)  # (10,)
fun(10, 30)  # (10, 30)
show(a=10, b=20, c='cc')  # {'a': 10, 'b': 20, 'c': 'cc'}

# 总结
def fun(a, b, c):
    print('a=', a)
    print('b=', b)
    print('c=', c)


fun(10, 20, 30)  # 函数调用时的参数传递,称为位置传参
l = [11, 22, 33]
fun(*l)  # 在函数调用时,将列表中的每个元素都转换为位置实参传入
fun(a=100, b=200, c=300)  # 函数调用时,成为关键字实参
d = {'a': 111, 'b': 222, 'c': 333}
fun(**d)  # 在函数调用时,将字典中的键值对都转换为关键字实参传入

def f(a, b=10):
    print('a=', a)
    print('b=', b)

def f1(*args):  # 个数可变的位置形参
    print(args)

def f2(**args):  # 个数可变的关键字形参
    print(args)

f1(11, 22, 33, 44)
f2(a=11, b=22, c=33)

def f3(a, b, *, c, d):  # 从*后的参数在函数调用时,只能采用关键字传递
    print('a=', a)
    print('b=', b)
    print('c=', c)
    print('d=', d)

#f3(10, 20, 30, 40)  # 位置实参传递
f3(a=10, b=20, c=30, d=40)
f3(10, 20, c=30, d=40)

# 函数定义时的形参的顺序问题
def func(a, b, *, c, d, **args):
    pass
def func2(*args, **args2):
    pass
def func3(a, b=10, *args, **args2):
    pass


# 变量的作用域
# 局部变量--在函数内部定义并使用的变量,只在函数内部有效,
#    局部变量使用global声明,这个变量就会成为全局变量
# 全局变量--函数体外定义的变量,可作用于函数内外


# 递归函数
# 如果在一个函数的函数体内调用了该函数本省,这个函数就成为递归函数
# 递归函数由 递归调用和递归终止条件组成
# 每递归调用一次函数,都会在栈内存分配一个栈帧。每执行完一次函数,都会释放相应的空间
# 缺点--占用内存多,效率低下。优点--思路和代码简单


# 阶乘
def fun(n):
    if n == 1:
        return 1
    else:
        return n * fun(n - 1)

print(fun(6))


# 斐波那契数列
def fib(n):  # 第n个位置上的数
    if n == 1:
        return 1
    elif n == 2:
        return 1
    else:
        return fib(n - 1) + fib(n - 2)

print(fib(6))
for i in range(1, 7):
    print(fib(i), end=' ')


# bug 分析
lst = [{'rating': [9.5, 1528760], 'id': 1291546, 'type':['剧情', '爱情'], 'title': '霸王别姬', 'actors': ['张国荣', '张丰毅', '巩俐']},
       {'rating': [9.6, 1528761], 'id': 1291447, 'type':['剧情', '爱情'], 'title': '阿甘正传', 'actors': ['汤姆.汉克斯', '罗宾.怀特']},
       {'rating': [9.5, 1528762], 'id': 1290549, 'type':['剧情', '悬疑'], 'title': '那个杀手不太冷', 'actors': ['--']}]

name = input('pls input the name of actors:')
for i in lst:  # 遍历列表 -->{} i是一个又一个的字典
    actor = i['actors']
    #for item in i:  # 遍历字典 -->key-value item是字典中一个key
    if name in actor:
        print(name + '演了:' + i['title'])
        break
else:
    print('not found the movie')


'''多个except结构'''
# 捕获异常的顺序按照先子类后父类的顺序,为了避免遗漏可能出现的异常,可以在最后增加BaseException
try:
    a = int(input('pls input a integer:'))
    b = int(input('pls input b integer:'))
    print(a/b)
except ZeroDivisionError:
    print('sorry')
except ValueError:
    print('string is not ok')
except BaseException as e:
    print(e)

print('the prog is running...')
print('exit')


'''try...except...else结构'''
# 如果try块中没有抛出异常,则执行else块,如果try块抛出异常,则执行except块
try:
    a = int(input('pls input a integer:'))
    b = int(input('pls input b integer:'))
    result = a/b
except BaseException as e:
    print(e)
else:
    print('result is', result)

print('the prog is running...')
print('exit')


'''try...except...else...finally结构'''
#         try
#    ______|______
#   |             |
# except         else
#   |_____________|
#          |
#        finally
# 如果try块中没有抛出异常,则执行else块;如果try块抛出异常,则执行except块
# finally块无论是否发生异常都会被执行,常用来释放try块中申请的资源
try:
    a = int(input('pls input a integer:'))
    b = int(input('pls input b integer:'))
    result = a/b
except BaseException as e:
    print('an error!')
    print(e)
else:
    print('result is', result)
finally:
    print('finally will run')

print('the prog is running...')
print('exit')

# python常见异常类型
# ZeroDivisionError 除(或取模)零(所有数据类型)
# IndexError  序列中没有此索引(index)
# KeyError  映射中没有这个键
# NameError  未声明/初始化对象(没有属性)
# SyntaxError  语法错误
# ValueError  传入无效的参数

# traceback模块打印异常信息
import traceback

try:
    print('----------')
    print(10/0)
except:
    traceback.print_exc()


'''
-------------------------------
------------ 类 class ----------
-------------------------------
'''
# 类的组成
# 类属性,实例属性,实例方法,静态方法,类方法

class Student:
    # 类属性
    native_place = 'jilin'

    def __init__(self, name, age):
        self.name = name  # 实例属性
        self.age = age

    # 实例方法,传self
    # 在类之外定义的称为函数,在类之内定义的称为方法
    def eat(self):
        print('student is eating')

    # 静态方法,什么都不传
    @staticmethod
    def sm():
        print('use static method')

    # 类方法,传cls
    @classmethod
    def cm(cls):
        print('class method')


# 在类之外定义的称为函数
def drink():
    print('drink water')


# 对象的创建又称为类的实例化
stu = Student('alice', 20)
print(type(stu))
print(id(stu))
print(stu)
print('--------')
print(type(Student))
print(id(Student))
print(Student)
print('----调用----')
stu.eat()  # 对象名.方法名()
print(stu.name)
print(stu.age)
Student.eat(stu)  # 这行与前三行代码功能相同,都是调用Student中的eat方法
                  # 类名.方法名(类的对象)-->实际上就是方法定义处的self

print(Student.native_place)  # 访问类属性
Student.cm()  # 访问类方法
Student.sm()  # 访问静态方法

stu1 = Student('tom', 10)
stu2 = Student('thomas', 12)
Student.native_place = '天津'
print(stu1.native_place)
print(stu2.native_place)

stu1.cm()  # ??
stu2.cm()
stu1.sm()  # ??
stu2.sm()


# 动态绑定属性和方法
# python 是动态语言,在创建对象之后,可以动态地绑定属性和方法
def show():
    print('I am a function')


stu1 = Student('alice', 20)
stu2 = Student('tom', 12)

# 为stu1动态绑定性别,属性
stu1.gender = 'female'
print(stu1.name, stu1.age, stu1.gender)
# 为stu1动态绑定方法
stu1.show = show
stu1.show()

# 没有为stu2动态绑定gender属性
#print(stu2.name, stu2.age, stu2.gender)  # AttributeError: 'Student' object has no attribute 'gender'
#stu2.show()  # AttributeError: 'Student' object has no attribute 'show'


'''
-------------------------------
------------ 面向对象 -----------
-------------------------------
'''
# 面向对象的三大特征
# 封装--提高程序的安全性
#      1)将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,
#         在类对象的外部调用方法。这样,无需关心方法内部的具体实现细节,从而隔离了复杂度。
#      2)在python中没有专门的修饰符用于属性的私有,如果该属性不希望在类对象外部被访问,前边使用‘__’
# 继承--提高代码的复用性
# 多态--提高程序的可扩展性和可维护性

# 封装
class Student:
    def __init__(self, name, age):
        self.name = name
        self.__age = age

    def show(self):
        print(self.name, self.__age)


stu = Student('alice', 20)
stu.show()
print(stu.name)
#print(stu.__age)  # AttributeError: 'Student' object has no attribute '__age'
print(dir(stu))
print(stu._Student__age)  # 在类的外部可以通过 classname.__privateAttribute


# 继承
# class ClassName(ParentClassName1, ParentClassName2...):
#     pass
# 如果一个类没有继承任何类,则默认继承了object
# python支持多继承
# 定义子类时,必须在其构造函数中调用父类的构造函数

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def info(self):
        print('name:{0}, age:{1}'.format(self.name, self.age))


class Student(Person):
    def __init__(self, name, age, score):
        super().__init__(name, age)
        self.score = score


class Teacher(Person):
    def __init__(self, name, age, teachofyear):
        super().__init__(name, age)
        self.teachofyear = teachofyear


stu = Student('alice', 18, 100)
stu.info()

# 多继承
class A(object):
    pass
class B(object):
    pass
class C(A, B):
    pass


# 方法重写
# 如果子类对继承自父类的某个属性或方法不满意,可以在子类中对其(方法体)进行重新编写
# 子类重写后的方法中可以通过super().xxx()调用父类中被重写的方法
class Car(object):
    def __init__(self, brand, price):
        self.brand = brand
        self.price = price
    def info(self):
        print(f'{self.brand} car price is {self.price}')

class ElectricCar(Car):
    def __init__(self, brand, price, power):
        super().__init__(brand, price)
        self.power = power
    def info(self):
        super().info()
        print(f'power is {self.power}')

class PatrolCar(Car):
    def __init__(self, brand, price, period):
        super().__init__(brand, price)
        self.period = period
    def info(self):
        super().info()
        print(f'maintain period is {self.period}')

car1 = ElectricCar('吉利', 100000, 'electric')
car2 = PatrolCar('宝马', 500000, 'one year')
car1.info()
car2.info()

# object类
# 1. object类是所有类的父类,因此所有类都有object类的属性和方法
# 2. 内置函数dir()可以查看指定对象所有属性
# 3. object有一个__str__()方法,用于返回一个对于“对象的描述”,
#    对应于内置函数str()经常用于print()方法,帮我们查看对象的信息,所以经常重写__str__()

class Student(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return 'rewrite the __str__'

    def info(self):
        print('the self info is')


stu = Student('alice', 12)
print(dir(stu))
print(stu)  # 默认调用__str__()这样的方法,未重写时输出为:<__main__.Student object at 0x0000020D534C7FD0>,重写后输出重写信息


# 多态
# 简单地说,多态就是"具有多种形态",它指的是:即便不知道一个变量所引用的对象到底是什么类型, 依然可以通过这个变量调用方法.
#   在运行过程中根据变量所引用对象的类型,动态决定调用哪个对象中的方法.
class Animal(object):
    def eat(self):
        print('动物会吃')

class Dog(Animal):
    def eat(self):
        print('狗吃骨头')

class Cat(Animal):
    def eat(self):
        print('猫吃鱼')

class Person:
    def eat(self):
        print('人吃饭饭')

def fun(obj):
    obj.eat()


fun(Cat())
fun(Dog())
fun(Person())

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


# 特殊方法和特殊属性

# 特殊属性
# __dict__ 获得类对象或实例对象所绑定的所有属性和方法的字典
class A(object):
    pass

class B(object):
    pass

class C(A, B):
    def __init__(self, name, age):
        self.name = name
        self.age = age

class D(A):
    pass

x=C('alice', 20)
print(x.__dict__)  # 查看实例对象x绑定的属性 {'name': 'alice', 'age': 20}
print(C.__dict__)  # 类对象的方法字典 {'__module__': '__main__', '__init__': <function C.__init__ at 0x0000025AA0FDF370>, '__doc__': None}
print(x.__class__)  # 对象所属的类 <class '__main__.C'>
print(C.__bases__)  # C类的父类类型的元组 (<class '__main__.A'>, <class '__main__.B'>)
print(C.__base__)  # C类的第一个父类类型 <class '__main__.A'>
print(C.__mro__)  # 查看类的层次结构 (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
print(A.__subclasses__())  # 子类的列表[<class '__main__.C'>, <class '__main__.D'>]

# 特殊方法
# __add__() 通过重写__add__()方法,可使用自定义对象具有"+"功能
# __len__() 通过重写__len__()方法,让内置函数len()的参数可以是自定义类型
# __new__() 用于创建对象
# __init__() 对创建的对象进行初始化

# __add__()
# 举例
a = 20
b = 100
c = a + b  # 两个整数类型的对象的相加操作,整数可以使用+运算符
d = a.__add__(b)  # 整数使用+运算符,其实就是整数对象的特殊方法
print(c)
print(d)

# 但是类对象或实例对象原本没有 + 的特殊方法,如果要使用就需自己定义
class Student:
    def __init__(self, name):
        self.name = name
    def __add__(self, other):
        return self.name + other.name
    def __len__(self):
        return len(self.name)

stu1 = Student('alice')
stu2 = Student('tom')
s = stu1+stu2  # 实现了两个对象的加法运算(因为在Student类中编写了__add__()特殊方法)
print(s)
s1 = stu1.__add__(stu2)  # 同上
print(s1)

# __len__()
# 举例
lst = [1, 2, 3, 4]
print(len(lst))  # 列表可以使用内置函数len
print(lst.__len__())  # 列表使用内置函数len,其实就是列表对象的一个特殊方法

# 但是类对象或实例对象原本没有 len 的特殊方法,如果要使用就需自己定义
print(stu1.__len__())

# __new__()
# 举例
class Person(object):

    def __new__(cls, *args, **kwargs):
        print('__new__被调用执行了,cls的id值为{0}'.format(id(cls)))
        obj = super().__new__(cls)
        print(f'创建的对象的id为:{id(obj)}')
        return obj

    def __init__(self, name, age):
        print('__init__被调用了,self的id值为:{0}'.format(id(self)))
        self.name = name
        self.age = age

print('object这个类对象的id值为 {0}'.format(id(object)))
print('Person这个类对象的id值为 {0}'.format(id(Person)))

p = Person('alice', 18)
print('p这个Person类的实例对象的id值为 {0}'.format(id(p)))
p.__new__(num=10, grade='a2', age=18)

# object这个类对象的id值为 140725928118144
# Person这个类对象的id值为 1563127210592
# __new__被调用执行了,cls的id值为1563127210592
# 创建的对象的id为:1563133868640
# __init__被调用了,self的id值为:1563133868640
# p这个Person类的实例对象的id值为 1563133868640
# new 在前创建对象,init 在后初始化对象

# 类的浅拷贝与深拷贝
# 1. 变量的赋值操作--只是形成两个变量,实际上还是指向同一个对象
# 2. 浅拷贝--python拷贝一般都是浅拷贝,拷贝时,对象包含的子对象内容不拷贝,因此,源对象与拷贝对象会引用同一个子对象
# 3. 深拷贝--使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象,源对象和拷贝对象所有的子对象也不相同

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)  # <__main__.Cpu object at 0x000002753BBB7FD0>
print(cpu2)  # <__main__.Cpu object at 0x000002753BBB7FD0>

# 2) 类的浅拷贝
# 结合内存图分析
disk = Disk()
computer = Computer(cpu1, disk)

import copy
computer2 = copy.copy(computer)
print(computer, computer.cpu)
print(computer2, computer2.cpu)

# 3) 类的深拷贝
# 结合内存图分析
computer3 = copy.deepcopy(computer)
print(computer, computer.cpu)
print(computer3, computer3.cpu)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值