Python基础-面向对象

第1章 面向对象介绍

1.1 面向对象与面向过程的区别

1.1.1 面向过程

面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好像精心设计好一条流水线,考虑周全什么时候处理什么东西。

  • 优点:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
  • 缺点:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
  • 应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等

1.1.2 面向对象

面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法)

  • 优点:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
  • 缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。
  • 应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等。

1.2 面向对象的优点

面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

1.3 面向对象中的几个专有名词

  • :具有相同特征的一类事物,如:人、狗、老虎
  • 对象/实例:具体的某一个事物,如:隔壁阿花、楼下旺财
  • 实例化:类—>对象的过程

1.4 面向对象的定义及调用格式

class 类名:
    def __init__(self,参数1,参数2):
        self.对象的属性1 = 参数1
        self.对象的属性2 = 参数2

    def 方法名1(self):pass

    def 方法名2(self):pass

对象名 = 类名(1,2)      # 对象就是实例,代表一个具体的东西
                        # 类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法
                        # 括号里传参数,参数不需要传self,其他与init中的形参一一对应
                        # 结果返回一个对象
对象名.对象的属性1      # 查看对象的属性,直接用 对象名.属性名 即可
对象名.方法名()         # 调用类中的方法,直接用 对象名.方法名() 即可

第2章 初识面向对象

2.1 类

2.1.1 类的声明

'''
class 类名:
    '类的文档字符串'
    类体
'''

# 创建一个类
class Data:
pass

【示例】:

class Person:                               # 定义一个人类
    role = 'person'                         # 人的角色属性都是人
    def __init__(self,name):
        self.name = name                    # 每一个角色都有自己的昵称

    def walk(self):                         # 人都可以走路,也就是有一个走路方法
        print("person is walking...")

2.1.2 类的实例化和属性引用

语法:对象名 = 类名(参数)

【示例】:类的实例化

leon = Person('leon')   # 类名()就等于在执行Person.__init__()
# 执行完__init__()就会返回一个对象。这个对象类似一个字典,存着属于这个人本身的一些属性和方法。

【示例】:类的属性引用

print(Person.role)  # 查看人的role属性
print(Person.walk)  # 引用人的走路方法,注意,这里不是在调用

【运行结果】:

person
<function Person.walk at 0x038E89C0>

2.1.3 关于self

self:在实例化时自动将对象/实例本身传给__init__的第一个参数。

2.1.4 查看类的属性存放位置

dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值

【示例】:

print(dir(Person))
print(Person.__dict__)

【运行结果】:

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'role', 'walk']
{'__module__': '__main__', 'role': 'person', '__init__': <function Person.__init__ at 0x02C188A0>, 'walk': <function Person.walk at 0x02C18930>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

2.1.5 特殊的类属性

类名.__name__       # 类的名字(字符串)
类名.__doc__        # 类的文档字符串
类名.__base__       # 类的第一个父类
类名.__bases__      # 类所有父类构成的元组
类名.__dict__       # 类的字典属性
类名.__module__     # 类定义所在的模块
类名.__class__      # 实例对应的类(仅新式类中)

2.2 对象

2.2.1 什么是对象

对象是关于类而实际存在的一个例子,即实例。

2.2.2 对象的属性引用

可以引用一个属性,也可以引用一个方法,因为方法也是一个属性,只不过是一个类似函数的属性,我们也管它叫动态属性。

引用动态属性并不是执行这个方法,要想调用方法和调用函数是一样的,都需要在后面加上括号。

【示例】:

print(leon.role)
print(leon.walk())

2.2.3 对象的交互

【示例】:

# 定义一个人类
class Person:
    role = 'person'

    def __init__(self,name,aggressivity,life_value,money):
        self.name = name                    # 角色名称
        self.aggressivity = aggressivity    # 角色攻击力
        self.life_value = life_value        # 角色血量
        self.money = money                  # 角色金钱数量

    def attack(self,dog):                   # 角色攻击狗的方法
        dog.life_value -= self.aggressivity

# 定义一个狗类
class Dog:
    role = 'dog'

    def __init__(self,name,breed,aggresivity,life_value):
        self.name = name                    # 狗名称
        self.breed = breed                  # 狗品种
        self.aggresivity = aggresivity      # 狗攻击力
        self.life_value = life_value        # 狗血量

    def bite(self,person):                  # 狗咬人的方法
        person.life_value -= self.aggresivity

# 实例化一个人和一只狗
leon = Person('leon',10,1000,1000)
ha2 = Dog('二哈','哈士奇',10,1000)

# 人打狗
print(ha2.life_value)
leon.attack(ha2)
print(ha2.life_value)

【运行结果】:

1000
990

2.3 类命名空间与对象、实例的命名空间

创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性,而类有两种属性:静态属性和动态属性。

  • 静态属性就是直接在类中定义的变量,类的数据属性是共享给所有对象的
print(id(Person.role) == id(leon.role))     # True
  • 动态属性就是定义在类中的方法,类的动态属性是绑定到所有对象的
print(Person.attack)    # <function Person.attack at 0x03AF89C0>
print(leon.attack)      # <bound method Person.attack of <__main__.Person object at 0x03A55690>>

Tips:创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性,在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类…最后都找不到就抛出异常。

2.4 面向对象的组合用法

2.4.1 组合的概念

组合指的是在一个类中以另外一个类的对象作为数据属性,称为类的组合,它是软件重用的重要方式。

2.4.2 组合使用的场景

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。

2.4.3 组合的示例

【示例】:人狗大战

class Weapon:
    def __init__(self,name,price,aggrev,life_value):
        self.name = name                    # 武器名称
        self.price = price                  # 武器价格
        self.aggrev = aggrev                # 武器攻击力
        self.life_value = life_value        # 武器让人增加的生命值

    def update(self,obj):                   # obj就是要带这个装备的人
        obj.money -= self.price             # 用这个武器的人花钱买所以对应的钱要减少
        obj.aggressivity += self.aggrev     # 带上这个装备可以让人增加攻击
        obj.life_value += self.life_value   # 带上这个装备可以让人增加生命值

    def prick(self,obj):                    # 这是该装备的主动技能,扎死对方
        obj.life_value -= 500               # 假设攻击力是500

lance = Weapon('长矛',200,6,100)

if leon.money > lance.price:                # 如果leon的钱比装备的价格多,可以买一把长矛
    lance.update(leon)                      # leon花钱买了一个长矛防身,且自身属性得到了提高
    leon.weapon = lance                     # leon装备上了长矛

print(ha2.life_value)
leon.attack(ha2)                            # leon打了ha2一下
print(ha2.life_value)
leon.weapon.prick(ha2)                      # leon发动长矛的主动技能攻击二哈
print(ha2.life_value)

【运行结果】:

1000
984
484

第3章 继承

3.1 初识继承

3.1.1 继承的概念

继承是一种创建新类的方式。在python中新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。

3.1.2 继承和抽象(先抽象再继承)

  • 抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类。

图片[1]|Python基础-面向对象|leon的博客

  • 继承是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

图片[2]|Python基础-面向对象|leon的博客

3.2 继承的格式

class ParentClass1:                             # 定义父类
    pass

class ParentClass2:                             # 定义父类
    pass

class SubClass1(ParentClass1):                  # 单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2):     # python支持多继承,用逗号分隔开多个继承的类
pass

# 查看类继承
print(ParentClass1.__bases__)   # (<class 'object'>,)
print(SubClass1.__bases__)  # (<class '__main__.ParentClass1'>,)
print(SubClass2.__bases__)  # (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

Tips:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

3.3 派生

子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),这就是派生。

Tips:一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。

【示例】:

class Animal:
    '''
    人和狗都是动物,所以创造一个Animal基类
    '''
    def __init__(self,name,aggressivity,life_value):
        self.name = name                    # 人和狗都有自己的昵称;
        self.aggressivity = aggressivity    # 人和狗都有自己的攻击力;
        self.life_value = life_value        # 人和狗都有自己的生命值;

    def eat(self):
        print('%s is eating'%self.name)

class Dog(Animal):
    '''
    狗类,继承Animal类
    '''
    def __init__(self,name,aggressivity,life_value,breed):
        super().__init__(name,aggressivity,life_value)  # 执行父类Animal的init方法
        self.breed = breed                              # 派生出了新的属性

    def bit(self,preson):
        '''
        派生出了新的技能:狗有咬人的技能
        :param preson:
        '''
        preson.life_value -=self.aggressivity

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

class Person(Animal):
    '''
    人类,继承Animal
    '''
    def __init__(self,name,aggressivity,life_value,money):
        super().__init__(name,aggressivity,life_value)
        #Animal.__init__(self, name, aggressivity, life_value)
        #super(Person, self).__init__(name, aggressivity, life_value)
        self.money = money

    def attack(self,dog):
        '''
        派生出了新的技能:人有攻击的技能
        :param dog:
        '''
        dog.life_value -= self.aggressivity

    def eat(self):
        print('from Person')

leon = Person('leon',10,1000,1000)
ha2 = Dog('二哈','哈士奇',10,1000)

print(leon.name)
print(ha2.name)

【运行结果】:

leon
二哈
from Person

3.4 继承的使用场景

  1. 继承基类的方法,并且做出自己的改变或者扩展(代码重用)
  2. 声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能。

Tips:接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。

【示例】:

# 使用abc模块后可以在实例刚初始化的时候判断是否存在规范中的方法,若不存在则报错
from abc import abstractclassmethod,ABCMeta
class Payment(metaclass=ABCMeta):
    @abstractclassmethod
    def pay(self,money): pass

class Wechat(Payment):
    def pay(self,money):
        print('已经用微信支付了%s元'%money)

class Alipay(Payment):
    def pay(self,money):
        print('已经用支付宝支付了%s元' %money)

class Applepay(Payment):
    # def pay(self,money):                              # 正确用法
    #     print('已经用applepay支付了%s元' %money)
    def zhifu(self,money):                              # 错误用法
        print('已经用applepay支付了%s元' %money)

wechat = Wechat()
ali = Alipay()
app = Applepay()    # 报错

【运行结果】:

Traceback (most recent call last):
  File "D:/WorkSpace/Python/python_study/面向对象/继承.py", line 111, in <module>
    app = Applepay()
TypeError: Can't instantiate abstract class Applepay with abstract methods pay

Tips:依赖倒置原则:

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程

3.5 抽象类

3.5.1 抽象类概念

抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。

从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。

从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的。

3.5.2 抽象类示例

import abc                  # 利用abc模块实现抽象类

class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod     # 定义抽象方法,无需实现功能
    def read(self):
        '子类必须定义读功能'
        pass

    @abc.abstractmethod     # 定义抽象方法,无需实现功能
    def write(self):
        '子类必须定义写功能'
        pass

# class Txt(All_file):
#     pass
#
# t1=Txt()                  # 报错,子类没有定义抽象方法

class Txt(All_file):        # 子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('文本数据的读取方法')

    def write(self):
        print('文本数据的读取方法')

class Sata(All_file):       # 子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('硬盘数据的读取方法')

    def write(self):
        print('硬盘数据的读取方法')

class Process(All_file):    # 子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('进程数据的读取方法')

    def write(self):
        print('进程数据的读取方法')

wenbenwenjian=Txt()
yingpanwenjian=Sata()
jinchengwenjian=Process()

# 这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)

【运行结果】:

文本数据的读取方法
硬盘数据的读取方法
进程数据的读取方法
file
file
file

3.5.3 抽象类和接口类区别

抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。

抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计。

在python中没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。

Tips:接口隔离原则:

使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。

  • 多继承问题:
  1. 抽象类:尽量避免多继承
  2. 继承接口:建议多继承接口
  • 方法的实现:
  1. 抽象类:可以对一些抽象方法做出基础实现
  2. 继承接口:任何方法都只是一种规范,具体的功能需要子类实现

3.6 继承顺序

新式类(当前类或父类继承了object类的类):广度优先

经典类:深度优先

【示例】:

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

class B(A):
    def test(self):
        print('from B')

class C(A):
    def test(self):
        print('from C')

class D(B):
    def test(self):
        print('from D')

class E(C):
    def test(self):
        print('from E')

class F(D,E):
    # def test(self):
    #     print('from F')
    pass

f1=F()
f1.test()

【运行结果】:

from D

【分析】:

图片[3]|Python基础-面向对象|leon的博客图片[4]|Python基础-面向对象|leon的博客

# 新式类继承顺序:F->D->B->E->C->A
# 经典类继承顺序:F->D->B->A->E->C
# python3中统一都是新式类
# pyhon2中才分新式类与经典类

【示例】:查看继承顺序表

print(F.mro())
print(F.__mro__) # 只有新式才有这个属性可以查看线性列表,经典类没有这个属性

【运行结果】:

[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

3.7 多态

多态指的是一类事物有多种形态,多态性是指在不考虑实例类型的情况下使用实例,python天生支持多态。

【示例】:

import abc
class Animal(metaclass=abc.ABCMeta):    # 同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal):                   # 动物的形态之一:人
    def talk(self):
        print('say hello')

class Dog(Animal):                      # 动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal):                      # 动物的形态之三:猪
    def talk(self):
        print('say aoao')

第4章 封装

4.1 封装介绍

4.1.1 封装定义

隐藏对象的属性和实现细节,仅对外提供公共访问方式。

4.1.2 封装好处

  1. 将变化隔离;
  2. 便于使用;
  3. 提高复用性;
  4. 提高安全性;

4.1.3 封装原则

  1. 将不需要对外提供的内容都隐藏起来;
  2. 把属性都隐藏,提供公共方法对其访问。

4.1.4 封装的用途

封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。

4.1.5 封装使用场景

  1. 隐藏起一个属性,不想让类的外部调用
  2. 保护这个属性,不想让属性随意被改变
  3. 保护这个属性,不被子类继承

4.1.6 封装方法

在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)。

4.2 私有变量和私有方法

4.2.1 私有变量示例

【示例】:

class A:
    __N = 0             # 类的数据属性就应该是共享的
# 但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X = 10   # 变形为self._A__X
    def __foo(self):    # 变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo()    # 只有在类内部才可以通过__foo的形式访问到

a = A()
a.bar()
print(a.__N)

【运行结果】:

from A
Traceback (most recent call last):
  File "D:/WorkSpace/Python/python_study/面向对象/封装.py", line 25, in <module>
    print(a.__N)
AttributeError: 'A' object has no attribute '__N'

【示例】:

class Room:
    def __init__(self,name,length,width):
        self.__name = name
        self.__length = length
        self.__width = width

    def get_name(self):
        return self.__name

    def set_name(self,newName):
        if type(newName) is str and newName.isdigit() == False:
            self.__name = newName
        else:
            print('不合法的姓名')

    def area(self):
        return self.__length * self.__width

r1 = Room('leon',2,1)
print(r1.area())
r1.set_name('2')
print(r1.get_name())

【运行结果】:

2
不合法的姓名
leon

4.2.2 私有方法示例

【示例】:

class A:
    def __fa(self):
        print('from A')
    def test(self):
        self.__fa()

class B(A):
    def __fa(self):
        print('from B')

b = B()
b.test()

【运行结果】:

from A

4.2.3 私有本质(自动变形)

【注意】:

  1. 这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如_A__N
  2. 变形的过程只在类的内部生效,在定义后的赋值操作,不会变形

【特点】:

  1. 类中定义的__x只能在内部使用,如__x,引用的就是变形的结果。
  2. 这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
  3. 在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

4.3 封装的扩展

【示例】:

# 类的设计者
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high

    def tell_area(self): # 对外提供的接口,隐藏了内部的实现细节,此时求的是面积
        return self.__width * self.__length

r1=Room('卧室','leon',20,20,20)
print(r1.tell_area()) # 使用者调用接口tell_area

# 类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high

    def tell_area(self): # 对外提供的接口,隐藏内部实现,此时求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
        return self.__width * self.__length * self.__high

r2=Room('卧室','leon',20,20,20)
print(r2.tell_area())

【运行结果】:

400
8000

第5章 面向对象相关内置函数

5.1 property修饰器

5.1.1 property修饰器作用

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则。简单地说就是将类中的方法伪装成属性。

5.1.2 property示例

【示例】:

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

    @property
    def name(self):                 # 此处name为伪装后属性的名称
        return self.__name

    @name.deleter                   # 此处name必须与@property下的方法名称相同
    def name(self):                 # 此处name必须与@property下的方法名称相同
        # del self.__name
        print('你不能删除这个属性')

    @name.setter                    # 此处name必须与@property下的方法名称相同
    def name(self,newName):         # 此处name必须与@property下的方法名称相同
        self.__name = newName

leon= Person('leon')
print(leon.age)
del leon.age                        # 此处执行的是系统默认的del方法
leon.name = 'shadow'                # 此处执行的是@name.setter下的方法
del leon.name                       # 此处执行的是@name.deleter下的方法
print(leon.name)
print(leon.age)

【运行结果】:

20
你不能删除这个属性
shadow
Traceback (most recent call last):
  File "D:/WorkSpace/Python/python_study/面向对象/封装.py", line 114, in <module>
    print(leon.age)
AttributeError: 'Person' object has no attribute 'age

5.2 classmethod修饰器

5.2.1 使用场景

当这个方法的操作只涉及静态属性的时候就应该使用classmethod来装饰这个方法。

5.2.2 classmethod示例

【示例】:

class Goods:
    __discount = 0.8
    def __init__(self,name,price):
        self.name = name
        self.__price = price

    @property
    def price(self):
        return self.__price * Goods.__discount

    @classmethod   # 把一个方法 变成一个类中的方法,这个方法就直接可以被类调用,不需要依托任何对象
    def change_discount(cls,new_discount):  # 修改折扣
        cls.__discount = new_discount

apple = Goods('苹果',5)
print(apple.price)
Goods.change_discount(0.5)   # Goods.change_discount(Goods)
print(apple.price)

【运行结果】:

4.0
2.5

5.3 staticmethod修饰器

5.3.1 使用场景

在完全面向对象的程序中,如果一个函数既和对象没有关系也和类没有关系,那么就用staticmethod将这个函数变成一个静态方法。

5.3.2 staticmethod示例

【示例】:

class Login:
    def __init__(self,name,password):
        self.name = name
        self.pwd = password

    def login(self):pass

    @staticmethod
    def get_usr_pwd():   # 静态方法
        usr = input('用户名 :')
        pwd = input('密码 :')
        Login(usr,pwd)

Login.get_usr_pwd()

【运行结果】:

用户名 :leon
密码 :123456

5.3.3 类方法和静态方法小结

  1. 类方法和静态方法都是类调用的
  2. 对象可以调用类方法和静态方法,但是一般情况下推荐用类名调用
  3. 类方法有一个默认参数cls,代表这个类
  4. 静态方法没有默认的参数,就象函数一样

5.4 isinstance方法

isinstance(obj,cls)检查是否obj是否是类 cls 的对象。

【示例】:

class Foo(object):
    pass

obj = Foo()
print(isinstance(obj, Foo))

【运行结果】:

True

5.5 issubclass方法

issubclass(sub, super)检查sub类是否是 super 类的派生类

【示例】:

class Foo(object):
    pass

class Bar(Foo):
    pass

print(issubclass(Bar, Foo))

【运行结果】:

True

第6章 反射

6.1 放射的概念

主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。python面向对象中的反射:通过字符串的形式操作对象相关的属性。

6.2 放射应用示例

【示例】:

class Teacher:
    dic = {'查看学生信息':'show_student','查看讲师信息':'show_teacher'}

    def show_student(self):
        print('This is show_student')

    def show_teacher(self):
        print('This is show_teacher')

    @classmethod
    def func(cls):
        print('This is classmethod')

leon = Teacher()
for k in Teacher.dic:
    print(k)
key = input('输入需求 :')

print(Teacher.dic[key])
if hasattr(leon,Teacher.dic[key]):
    func = getattr(leon,Teacher.dic[key])
    func()

【运行结果】:

查看学生信息
查看讲师信息
输入需求 :查看学生信息
show_student
This is show_student

6.3 反射对象中的属性和方法

6.3.1 反射对象的属性

【示例】:

class A:
    def func(self):
        print('in func')

a = A()
a.name = 'leon'
a.age = 63

ret = getattr(a,'name')  # 通过变量名的字符串形式取到的值
print(ret)
print(a.__dict__)

funcname = input('>>>')   # func
print(getattr(a,funcname))
print(a.__dict__[funcname])

【运行结果】:

leon
{'name': 'leon', 'age': 63}
>>>name
leon
leon

6.3.2 反射对象的方法

【示例】:

class A:
    def func(self):
        print('in func')

a = A()
a.name = 'leon'
a.age = 63

a.func()
ret = getattr(a,'func')
ret()

【运行结果】:

in func
in func

6.4 反射类的属性和方法

6.4.1 反射类的属性

【示例】:

class A:
    price = 20

    @classmethod
    def func(cls):
        print('in func')

# 反射类的属性
print(getattr(A,'price'))

【运行结果】:

20

6.4.2 反射类的方法

【示例】:

class A:
    price = 20

    @classmethod
    def func(cls):
        print('in func')

#反射类的方法 :classmethod staticmethod
A.func()
if hasattr(A,'func'):
    getattr(A,'func')()

【运行结果】:

in func
in func

6.5 反射模块的属性和方法

6.5.1 反射内置模块的属性和方法

【示例】:

import time
print(getattr(time,'time')())
print(getattr(time,'asctime')())

【运行结果】:

1538623510.1218064
Thu Oct  4 11:25:10 2018

6.5.2 反射自定义模块的变量和函数

【示例】:

def test():
    print('test')
year = 2018

import sys
print(sys.modules['__main__'].year)
# 反射自己模块中的变量
print(getattr(sys.modules['__main__'],'year'))

# 反射自己模块中的函数
getattr(sys.modules['__main__'],'test')()
funcname = input('>>>')
print(getattr(sys.modules[__name__],funcname))

【运行结果】:

2018
2018
test
>>>test
<function test at 0x02BC89C0>

6.5.3 反射的函数带参数

【示例】:

import time
print(time.strftime('%Y-%m-%d %H:%M:S'))
print(getattr(time,'strftime')('%Y-%m-%d %H:%M:S'))

【运行结果】:

2018-10-04 11:29:S
2018-10-04 11:29:S

6.6 setattr和delatter

【示例】:

class A:
    pass
a = A()
setattr(a,'name','leon')
setattr(A,'name','shadow')
print(A.name)
print(a.name)

# delattr 删除一个变量
delattr(a,'name')
print(a.name)
delattr(A,'name')
print(a.name)

【运行结果】:

shadow
leon
shadow
Traceback (most recent call last):
  File "D:/WorkSpace/Python/python_study/面向对象/反射.py", line 108, in <module>
    print(a.name)
AttributeError: 'A' object has no attribute 'name'
温馨提示:本文最后更新于2022-12-20 20:57:46,已超过493天没有更新。某些文章具有时效性,若文章内容或图片资源有错误或已失效,请联系站长。谢谢!
转载请注明本文链接:https://blog.leonshadow.cn/763482/1277.html
© 版权声明
THE END
喜欢就支持一下吧
点赞1 分享