Python基础(第七周)

目录

一、装饰器补充内容

装饰器带参数

二、面向对象

1.面向对象和面向过程

2.类与对象

3.面向对象的基础语法

(1)创建类(只包含方法)

(2)创建对象

(3)初始化方法介绍

(4)__del__方法介绍

(5)__str__方法

4.面向对象的封装

5.面向对象的私有属性和私有方法

6.面向对象的继承

使用继承开发:

方法的重写

多继承

7.多态


一、装饰器补充内容

装饰器带参数

我们先写一个带参数的装饰器,再对其进行解释

def func_arg(name):
    def func_out(func):
        def wrapped_func():
            print('这是一个装饰器')
            print('正在执行被装饰的函数...')
            func()
            print('正在输出装饰器的参数...')
            print(name)

        return wrapped_func

    return func_out


@func_arg('world')
def f():
    print('hello')


f()
'''
这是一个装饰器
正在执行被装饰的函数...
hello
正在输出装饰器的参数...
world
'''

1.执行@func_arg('world'),调用func_arg函数,并将‘world’作为参数传递给name。调用后进入func_arg内部进行从上到下执行程序。

2.执行func_arg内部的的def func_out(func):,定义了func_outer函数,将func_out函数加载到内存中

3.执行func_arg内部的return func_out,将func_out函数的引用返回给func_arg函数的调用者:@func_arg('world'),所以此时代码的逻辑变成了

def func_out(func):
    def wrapped_func():
        print('这是一个装饰器')
        print('正在执行被装饰的函数...')
        func()
        print('正在输出装饰器的参数...')
        print(name)    # name的值已经在内存中有了

    return wrapped_func


@func_out
def f():
    print('hello')


f()

到这里就是我们熟悉的正常装饰器了

二、面向对象

1.面向对象和面向过程

面向过程:

面向过程不同于面向对象,面向过程分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。 

面向对象:

面向对象就是构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

区别:

简单来说:用面向过程的方法写出来的程序是一份蛋炒饭,而用面向对象写出来的程序是一份盖浇饭。所谓盖浇饭,就是在米饭上面浇上一份盖菜,你喜欢什么菜,你就浇上什么菜。我觉得这个比喻还是比较贴切的。

蛋炒饭制作的细节,我不太清楚,因为我没当过厨师,也不会做饭,但最后的一道工序肯定是把米饭和鸡蛋混在一起炒匀。盖浇饭呢,则是把米饭和盖菜分别做好,你如果要一份红烧肉盖饭呢,就给你浇一份红烧肉;如果要一份青椒土豆盖浇饭,就给浇一份青椒土豆丝。

蛋炒饭的好处就是入味均匀,吃起来香。如果恰巧你不爱吃鸡蛋,只爱吃青菜的话,那么唯一的办法就是全部倒掉,重新做一份青菜炒饭了。盖浇饭就没这么多麻烦,你只需要把上面的盖菜拨掉,更换一份盖菜就可以了。盖浇饭的缺点是入味不均,可能没有蛋炒饭那么香。

到底是蛋炒饭好还是盖浇饭好呢?其实这类问题都很难回答,非要比个上下高低的话,就必须设定一个场景,否则只能说是各有所长。那么从饭馆角度来讲的话,做盖浇饭显然比蛋炒饭更有优势,他可以组合出来任意多的组合,而且不会浪费。

盖浇饭的好处就是“菜”“饭”分离,从而提高了制作盖浇饭的灵活性。饭不满意就换饭,菜不满意换菜。用软件工程的专业术语就是“可维护性”比较好,“饭” 和“菜”的耦合度比较低。蛋炒饭将“蛋”“饭”搅和在一起,想换“蛋”“饭”中任何一种都很困难,耦合度很高,以至于“可维护性”比较差。软件工程追求的目标之一就是可维护性,可维护性主要表现在3个方面:可理解性、可测试性和可修改性。面向对象的好处之一就是显著的改善了软件系统的可维护性。

2.类与对象

类:具有相同特征(在编程中是:属性)和行为(在编程中是:方法)的东西

类通俗理解可以是制造飞机的图纸和模板(不可以直接使用)

对象:由类创建出来的具体存在的东西(可以直接使用),由哪一类创建就具有哪一类的属性和方法

当我们拿到一个需求时,首先要分析需要创建哪些类:比如开发一个植物大战僵尸游戏,最主要的类有:植物类和僵尸类,然后分析每一类的属性(即这一类具有什么相同的特征)和方法(即这一类具有什么相同的行为)

注意:类的命名要满足大驼峰命名法:每一个单词的首字母都要大写

3.面向对象的基础语法

(1)创建类(只包含方法)

class 类名:
    
    def 方法1(self, 参数列表):
        pass

    def 方法2(self, 参数列表):
        pass

self是必须参数,self代表对象本身

(2)创建对象

对象名 = 类名()

例:小猫叫爱吃鱼,小猫在喝水

class Cat:

    def eat(self):
        print('小猫在吃鱼')

    def drink(self):
        print('小猫在喝水')


little_cat = Cat()
little_cat.eat()  # 小猫在吃鱼
little_cat.drink()  # 小猫在喝水

如果这只小猫有名字,叫Tom,俨然他的名字就是一个属性,此时我们可以修改代码

class Cat:

    def eat(self):
        print(f'{self.name}在吃鱼')

    def drink(self):
        print(f'{self.name}在喝水')


little_cat = Cat()
little_cat.name = 'Tom'
little_cat.eat()  # Tom在吃鱼
little_cat.drink()  # Tom在喝水

上面这种是在类的外面给对象添加属性,如果将代码修改成下面这样,他就会报错,所以在类的外面添加属性不推荐

class Cat:

    def eat(self):
        print(f'{self.name}在吃鱼')

    def drink(self):
        print(f'{self.name}在喝水')


little_cat = Cat()
little_cat.eat() 
little_cat.drink()  
little_cat.name = 'Tom'

(3)初始化方法介绍

class Cat:
    def __init__(self):
        print('这是一个初始化方法')

    def eat(self):
        print(f'{self.name}在吃鱼')

    def drink(self):
        print(f'{self.name}在喝水')


little_cat = Cat()  # 这是一个初始化方法

当创建对象时会自动调用初始化方法:__init__方法

所以我们可以通过给初始化方法传递参数去给类添加属性,这样在创建对象时会自动初始化他的属性

class Cat:
    def __init__(self,name):
     # 对象的属性名=参数名
        self.name = name

    def eat(self):
        print(f'{self.name}在吃鱼')

    def drink(self):
        print(f'{self.name}在喝水')


little_cat = Cat('Tom')  
little_cat.eat()  # Tom在吃鱼
little_cat.drink()  # Tom在喝水

big_cat = Cat('Jerry')
big_cat.eat()  # Jerry在吃鱼
big_cat.drink()  # Jerry在喝水

(4)__del__方法介绍

对象在从内从地址中销毁之前会自动调用此方法(可以理解为:临死前做的最后一点贡献)

class Cat:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f'{self.name}在吃鱼')

    def drink(self):
        print(f'{self.name}在喝水')

    def __del__(self):
        print(f'{self.name}跑了')


little_cat = Cat('Tom')
little_cat.eat()  # Tom在吃鱼
little_cat.drink()  # Tom在喝水

big_cat = Cat('Jerry')
big_cat.eat()  # Jerry在吃鱼
big_cat.drink()  # Jerry在喝水

print('*' * 50)
'''
Tom在吃鱼
Tom在喝水
Jerry在吃鱼
Jerry在喝水
**************************************************
Tom跑了
Jerry跑了
'''

我们发现当程序执行完时,big_cat和little_cat被自动销毁,并且执行了__del__方法

我们也可以进行手动删除

class Cat:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f'{self.name}在吃鱼')

    def drink(self):
        print(f'{self.name}在喝水')

    def __del__(self):
        print(f'{self.name}跑了')


little_cat = Cat('Tom')
little_cat.eat()  # Tom在吃鱼
little_cat.drink()  # Tom在喝水

big_cat = Cat('Jerry')
big_cat.eat()  # Jerry在吃鱼
big_cat.drink()  # Jerry在喝水
del little_cat
print('*' * 50)
'''
Tom在吃鱼
Tom在喝水
Jerry在吃鱼
Jerry在喝水
Tom跑了
**************************************************
Jerry跑了
'''

我们在打印*前手动删除了little_cat变量,他就执行了__del__方法,big_cat在程序执行完被自动销毁,执行__del__方法

(5)__str__方法

class Cat:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f'{self.name}在吃鱼')

    def drink(self):
        print(f'{self.name}在喝水')

    def __del__(self):
        print(f'{self.name}跑了')


little_cat = Cat('Tom')
print(little_cat)  # <__main__.Cat object at 0x000001832B989190>
little_cat.eat()  # Tom在吃鱼
little_cat.drink()  # Tom在喝水

我们在加入__str__方法之前,打印对象的变量名,会发现输出的是对象在内存中的地址

class Cat:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f'这只猫的名字叫{self.name}'

    def eat(self):
        print(f'{self.name}在吃鱼')

    def drink(self):
        print(f'{self.name}在喝水')

    def __del__(self):
        print(f'{self.name}跑了')


little_cat = Cat('Tom')
print(little_cat)  # 这只猫的名字叫Tom
little_cat.eat()  # Tom在吃鱼
little_cat.drink()  # Tom在喝水

当我们设置了__str__方法中return的内容后,再打印对象的变量名,输出就是__str__方法中return的内容

4.面向对象的封装

需求:小明体重75公斤,小明每次跑步会减肥0.5公斤,小明每次吃东西体重会增加1公斤

class Person:

    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def __str__(self):
        return f'我是{self.name},我的体重是{self.weight}'

    def run(self):
        # 调用一次,体重减0.5
        print('跑步...')
        self.weight -= 0.5

    def eat(self):
        # 调用一次,体重增加1
        print('吃...')
        self.weight += 1

    def __del__(self):
        print(f'{self.name}现在的体重是{self.weight}')


xiaoming = Person('小明', 75)
xiaoming.run()  # 跑步...
xiaoming.run()  # 跑步...
xiaoming.eat()  # 吃
print(xiaoming)  # 我是小明,我的体重是75.0
xiaoming.run()  # 跑步...
xiaoming.eat()  # 吃
xiaoming.eat()  # 吃
xiaoming.eat()  # 吃
print('*' * 50)
# 小明现在的体重是77.5

需求:房子(House)有户型,总面积和家具名称列表,新房子中没有任何的家具,家具(HouseItem)有家具名字和占地面积,其中席梦思(bed)占地4平米,衣柜(chest)占地2平米,餐桌(table)占地2.5平米,将以上三件家具添加到房子中,打印房子时,要求输出户型,总面积,剩余面积,家具名称列表

分析:

House类:属性:面积,户型,家具名称列表,剩余面积        方法:初始化方法,str方法,添加家具方法

HouseItem类:属性:名称,面积        方法:初始化方法,str方法

因为House类中要用到家具,所以先开发HouseItem类

class HouseItem:

    def __init__(self, name, area):
        """
        :param name: 家具名称
        :param area: 占地面积
        """
        self.name = name
        self.area = area

    def __str__(self):
        return '[%s] 占地面积是 %.2f 平米' % (self.name, self.area)


# 创建家具
bed = HouseItem('席梦思', 4)
chest = HouseItem('衣柜', 2)
table = HouseItem('餐桌', 2.5)
print(bed)
print(chest)
print(table)


class House:

    def __init__(self, house_type, area):
        """
        :param house_type: 户型
        :param area: 总面积
        """
        self.house_type = house_type
        self.area = area
        # 剩余面积默认和总面积保持一致
        self.free_area = area
        # 家具名称列表(默认没有任何家具)
        self.item_list = []

    def __str__(self):
        return ("户型:%s\n总面积:%.2f平米[剩余面积:%.2f平米]\n家具名称:%s"
                % (self.house_type, self.area,
                   self.free_area, self.item_list))

    def add_item(self, item):
        print("要添加%s" % item)

        # 1. 判断家具的面积是否大于剩余面积
        if item.area > self.free_area:
            print("要添加的%s面积过大,不能添加" % item.name)
            return

        # 2. 将家具的名称添加到列表中
        self.item_list.append(item.name)

        # 3. 计算剩余面积
        self.free_area -= item.area


my_home = House("两室一厅", 60)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)

print(my_home)

5.面向对象的私有属性和私有方法

对象 的 某些属性或方法 只希望 在对象的内部被使用,而 不希望在外部被访问到

定义:在属性名或者在方法名前面加两个下划线

class Girl:

    def __init__(self, name, age):
        self.name = name
        # 定义私有属性
        self.__age = age

    # 定义私有方法
    def __secret(self):
        print(f'{self.name}的年龄是{self.__age}')


xiaomei = Girl('小美', 20)
print(xiaomei.name)  # 小美
print(xiaomei.__age)  # 报错

xiaomei = Girl('小美', 20)
xiaomei.__secret()    # 报错

我们可以看到输出name属性可以正常输出,但我们明明定义了__age属性,却在输出时报错

class Girl:

    def __init__(self, name, age):
        self.name = name
        # 定义私有属性
        self.__age = age

    # 定义私有方法
    def __secret(self):
        print(f'{self.name}的年龄是{self.__age}')


xiaomei = Girl('小美', 20)
xiaomei.__secret()    # 报错

同样,在调用私有方式时也会报错

然而,在python中并没有真正的私有,我们还是有办法访问到私有属性和私有方法

class Girl:

    def __init__(self, name, age):
        self.name = name
        # 定义私有属性
        self.__age = age

    # 定义私有方法
    def __secret(self):
        print(f'{self.name}的年龄是{self.__age}')


xiaomei = Girl('小美', 20)
print(xiaomei._Girl__age)  # 20

我们在调用__age属性前加上 _类名,就可以使用私有属性

class Girl:

    def __init__(self, name, age):
        self.name = name
        # 定义私有属性
        self.__age = age

    # 定义私有方法
    def __secret(self):
        print(f'{self.name}的年龄是{self.__age}')


xiaomei = Girl('小美', 20)
xiaomei._Girl__secret()  # 小美的年龄是20

我们在调用__secret方法前加上 _类名,就可以调用私有方法

6.面向对象的继承

继承:子类拥有父类的所有属性和方法

class Animal:
    def eat(self):
        print('eat')

    def sleep(self):
        print('sleep')


class Dog:
    def eat(self):
        print('eat')

    def sleep(self):
        print('sleep')


dog = Dog()
dog.eat()  # eat
dog.sleep()  # sleep

狗属于动物,动物类可以吃和睡,狗类也可以吃和睡,狗既然属于动物类,那么是否有一种办法不用在狗类里面重新定义一个吃和睡的方法吗(避免代码的冗余)?这就要使用到继承

使用继承开发:

class Animal:
    def eat(self):
        print('eat')

    def sleep(self):
        print('sleep')


# 子类(继承的父类)
class Dog(Animal):
    # 可以添加子类特有的方法
    def bark(self):
        print('bark')


dog = Dog()
dog.eat()  # eat
dog.sleep()  # sleep
dog.bark()  # bark

可以看见我们在继承动物类的狗类中并没有定义eat和sleep方法,但可以调用父类(Animal类)中的eat和sleep方法

class Animal:
    def eat(self):
        print('eat')

    def sleep(self):
        print('sleep')


# 子类(继承的父类)
class Dog(Animal):
    # 可以添加子类特有的方法
    def bark(self):
        print('bark')


class XiaoTianQuan(Dog):
    def fly(self):
        print('fly')


dog = XiaoTianQuan()
dog.eat()  # eat
dog.sleep()  # sleep
dog.bark()  # bark
dog.fly()  # fly

我们又定义了一个哮天犬类让他继承于狗类,而狗类继承于动物类,我们发现哮天犬类的对象可以调用父类的父类的方法,说明继承具有传递性

方法的重写

当父类中的方法不满足子类的需求时,我们需要怎么办呢?

1.覆盖父类的方法

class Dog(Animal):
    # 可以添加子类特有的方法
    def bark(self):
        print('bark')


class XiaoTianQuan(Dog):
    def fly(self):
        print('fly')

比如哮天犬的叫和狗类的叫不一样,我们应该怎么办?

class Dog():
    # 可以添加子类特有的方法
    def bark(self):
        print('bark')


class XiaoTianQuan(Dog):
    def fly(self):
        print('fly')

    def bark(self):
        print('哮天犬的bark')


dog = XiaoTianQuan()
dog.bark()  # 哮天犬的bark

我们可以在哮天犬类中再定义一个bark方法,当执行dog.bark()时,会先找子类的bark方法,找到了就执行子类的bark方法,如果子类没有再去父类找bark方法。这就是方法重写的第一种:覆盖父类的方法

2.扩展父类方法

父类的方法不能完全满足需求(只能满足部分需求),就要在子类中对父类的方法进行扩展,这个如何实现呢?

class Dog():
    # 可以添加子类特有的方法
    def bark(self):
        print('bark')


class XiaoTianQuan(Dog):
    def fly(self):
        print('fly')

    def bark(self):
        super().bark()
        print('哮天犬的bark')


dog = XiaoTianQuan()
dog.bark()  
'''
bark
哮天犬的bark
'''

在子类中也定义一个bark方法,使用 super().父类方法名() 的形式对父类方法名进行调用,然后在增加子类特有的需求,这样最后在创建完对象后,调用bark方法,就会调用子类中拓展完父类bark的bark方法

注意:当父类中有私有方法和私有属性时,子类并不能直接继承父类的私有属性和私有方法,如果想使用就可以使用我们上面介绍的方法。

多继承

一个子类继承于多个父类就叫多继承

class A(object):
    def test(self):
        print('A---test')

    def demo(self):
        print('A---demo')


class B(object):
    def demo(self):
        print("B---demo")

    def test(self):
        print('B---test')


class C(B, A):
    pass


c = C()
c.test()   # B---test 
c.demo()   # B---demo

注意事项:当子类继承的多个父类中有名字相同的方法,并且我们调用了此方法时,会优先执行括号中第一个父类的该方法

7.多态

不同的子类调用相同的父类方法产生不同的结果叫做多态

前提条件:

1.继承:多态一定是发生在子类和父类之间;

2.重写:子类重写了父类的方法。

class WhoSay:
    def say(self,who):
        who.say()
class CLanguage:
    def say(self):
        print("调用的是 Clanguage 类的say方法")
class CPython(CLanguage):
    def say(self):
        print("调用的是 CPython 类的say方法")
class CLinux(CLanguage):
    def say(self):
        print("调用的是 CLinux 类的say方法")
a = WhoSay()
#调用 CLanguage 类的 say() 方法
a.say(CLanguage())
#调用 CPython 类的 say() 方法
a.say(CPython())
#调用 CLinux 类的 say() 方法
a.say(CLinux())
'''
调用的是 Clanguage 类的say方法
调用的是 CPython 类的say方法
调用的是 CLinux 类的say方法
'''

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不懂编程的大学生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值