Python 面向对象

1. 简介

对具象化的事物进行抽象, 尽管细节有偏差, 但与其他大部分编程语言的面向对象概念类似.

1.1 一个极简实例

class Animal(object):

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

    def describe(self):
        # return self.name + self.score
        print(f'名字:{self.name} | 颜色:{ self.color}')


# 注意到__init__方法的第一个参数永远是self,表示创建的实例本身,
# 因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
# 构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值
xialing = Animal('黠灵', '灰白')
xialing.describe()

# 名字:黠灵 | 颜色:灰白
# Process finished with exit code 0

1.2 魔术方法 __init__

class Human(object):
    def describe(self):
        pass

xialing = Human()
xialing.name= "黠灵"
xialing.ability= "飞翔"

huban = Human()
xialing.name= "虎斑"
xialing.ability= "瞬移"

...
...
...

 

  • 这其实是一个不是问题的问题,  初始化, 在其他语言和很多程序的设计中, 都会使用到.
  • 比如人, 有些属性是需要通过学习和机遇才能获得, 想象科学家和普通人的区别.
  • 有些属性则是人类天生就有的共性, 比如一出生就拥有了机体, 会睡觉, 会摄取营养.
  • 很显然, 作为实例/个体的人, 有许多共性, 都具有姓名, 年龄, 族别, 性别等.  如果为每一个实例去一个个添加这些共同的属性, 会非常麻烦.
class Human(object):
    def __init__(self, name, age, gender, nationality):
        self.name = name
        self.age = age
        self.gender = gender
        self.nationality = nationality

    def describe(self):
        pass

xialing = Human("黠灵", "6", "男", "汉") # 此时xialing 便具有所有的初始属性
print(xialing.name, xialing.age, xialing.gender, xialing.nationality )

望文生义 # initialization / initiate, 初始化,是一种特殊的方法。在创建对象时, 自动初始化对象,不需要手动/显式地调用. 即为对象成员变量赋初始值.

 

1.3 魔术方法 __new__

__init__ 的功能很明显, 其内部如何实现功能, 则与__new__相关.

Python 中实例化对象时, 不需要显式地使用new关键字 (其他语言比如PHP中则需要).

但实际上在实例化对象时, Python会自动效用__new__方法

 

class Animal(object):
    def __init__(self):
        print('init方法被执行')


    def __new__(cls, *args, **kwargs):
        print('new方法被执行')
        
        print(cls)                    # cls 是 Animal 类        
        print(object.__new__(cls) )   # 得到 Animal 类的对象     
    
        return super().__new__(cls)   # return object.__new__(cls) 这样写也行


xialing = Animal()

# new方法被执行
# init方法被执行
# Process finished with exit code 0

__new__ 先执行

__init__ 后执行

 

1.4 self 是什么?

class Person(object):
    def ability(self):
        print(self)
        print('self的内存地址:' + str(id(self)))

laozi = Person()
laozi.ability()
print("实例化得到的对象的内存地址:" + str(id(laozi)))

zhuangzi = Person()
zhuangzi.ability()
print("实例化得到的对象的内存地址:" + str(id(zhuangzi)))


# <__main__.Person object at 0x00000233A6D5D750>
# self的内存地址:2420865619792
# 实例化得到的对象的内存地址:2420865619792
#
# <__main__.Person object at 0x00000233A6D5D7D0>
# self的内存地址:2420865619920
# 实例化得到的对象的内存地址:2420865619920
#
# Process finished with exit code 0

 

由此可知:

  • self 和 当前对象指向相同的内存地址
  • self 是 对象的引用

 

1.5 析构方法 __del__

# 当一个对象被删除或销毁时, Python 解释器会默认调用__del__()方法, 它称之为析构方法.

class Animal(object):
    def __init__(self, name):
        self.name = name
        print("__init__ 方法被调用")


    def __del__(self):
        print("__del__ 方法被调用")
        print(f'{self.name}对象被销毁')

cat = Animal("kitty")
# print(input("请输入"))

# __init__ 方法被调用
# __del__ 方法被调用
# kitty对象被销毁
# Process finished with exit code 0

 

2. 继承

2.1 单继承: 继承的最简实例

class Animal(object):  # 创建一个基类
    def run(self):
        print('这是Animal基类内部的run方法')


class Dog(Animal):  # 一个Dog子类, 继承自Animal. 子类能获得父类的全部功能, 称之为继承
    pass

class Cat(Animal):  # 一个cat子类
    pass


xialing = Dog()  # 将子类实例化, 得到对象xialing, 验证该对象是否获得基类的方法
xialing.run()


# 这是Animal基类内部的run方法
# Process finished with exit code 0
  • Dog类继承自Animal类
  • Dog类实例化生成的对象xialing获得了Animal类的方法

 

2.2 多继承

class Animal(object):
    def climb(self):
        print('动物会爬树')


class Human(object):
    def talk(self):
        print("人类会说话")


class Immortal(object):
    def magic(self):
        print("神仙会仙术")


class Monkey(Animal, Human, Immortal):
    pass

wukong = Monkey()
wukong.climb()
wukong.talk()
wukong.magic()

# 动物会爬树
# 人类会说话
# 神仙会仙术
# Process finished with exit code 0
  • Monkey类 继承了三个基类
  • Monkey类实例化的对象获得了三个类的方法

 

2.2.1 多继承里的查找顺序问题

 

class Animal(object):
    def climb(self):
        print('动物会爬树')


class Human(Animal):
    def climb(self):
        print("人类会会爬树")


class Immortal(Animal):
    def climb(self):
        print("神仙也会爬树")


class Monkey(Human, Immortal):
    pass

wukong = Monkey()
wukong.climb()
print(Monkey.__mro__)

# 人类会会爬树
# (<class '__main__.Monkey'>, 
# <class '__main__.Human'>, 
# <class '__main__.Immortal'>,
# <class '__main__.Animal'>, 
# <class 'object'>)

# Process finished with exit code 0
  • 自下往上, 由近及远的去查找climb方法. (注意monkey类的继承顺序, Human在前)
  • 如果Monkey类里增加climb 方法, 并print(“猴子当然会爬树”), 那么最终的输出结果是猴子当然会爬树.
  • 可通过 __mro__ 来查看python内部的查找顺序
  • 这个涉及到语言本身的算法, 如广度优先.

2.2.2 方法重写

适用条件

  • 参数列表必须完全和被重写方法的参数列表一致. 如果Human类的__init__方法中添加其他参数, 则会报错. TypeError: Animal.__init__() takes 3 positional arguments but 4 were given
  • 返回类型必须完全和被重写方法的返回类型一致.
  • 访问修饰符的限制一定要大于被重写方法的访问修饰符.

 

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

    def describe(self):
        print('动物会爬树')


class Human(Animal):
    def __init__(self, name, age):

        # Animal.__init__(self, name, age)    # 手动调用
        super().__init__(name, age)     # super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。
        self.title = "齐天大圣"          # 拓展其他的属性

    def describe(self):
        print(self.name)
        print(self.age)
        print(self.title)

wukong = Human('悟空', '99999')
wukong.describe()


# 悟空
# 99999
# 齐天大圣
# Process finished with exit code 0

 

2.2.3 方法重载

重载是让类以统一的方式处理不同类型数据的一种手段。

适用条件:

  • 一个类里面
  • 方法名字相同
  • 参数不同

基本设计本原则:

  • 仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载

解决的问题:

  • 可变参数类型
  • 可变参数个数

由此可知:

  • 函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数.
  • 函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。

进而得到:

  • Python 里并没有语言规范上的方法重载.
  • 但有其他语言里方法重载里的功能
  • 因为Python函数自身的独特性和灵活性,  已经可以实现其他语言里的方法重载功能.

 

3. 多态

 

定义: 同一种行为 , 对于不同的子类对象有不同的行为表现

适用条件:

  • 继承:多态必须发生在父类和子类之间
  • 重写: 子类重写父类的方法
class Animal(object):  # 创建一个基类
    def describe(self):
        print('这是Animal基类')

class Dog(Animal):
    def describe(self):
        print('这是继承Animal类的Dog类, 这里是狗')


class Cat(Animal):  # 子类
    def describe(self):
        print('这是继承Animal类的Cat类, 这里是猫')

xialing = Dog()
xialing.describe()

lihua = Cat()
lihua.describe()

# 这是继承Animal类的Dog类, 这里是狗
# 这是继承Animal类的Cat类, 这里是猫
# Process finished with exit code 0

 

  • 黠灵 之于Animal类, 得到了Dog子类的方法.
  • 狸花 之于Animal类, 得到了Cat子类的方法.
  • 同一个机器, 能根据需求制造出不同的产品
    • Animal类  = 机床
      • 重写和继承 = 调整和设计程序
        • 第一次加工出齿轮/黠灵
        • 第二次加工出叶片/狸花猫

3.1 多态的优越性

 

class Animal(object):  # 创建一个基类
    def describe(self):
        print('这是Animal基类')

class Dog(Animal):
    def describe(self):
        print('这是继承Animal类的Dog类, 这里是狗')


class Cat(Animal):  # 子类
    def describe(self):
        print('这是继承Animal类的Cat类, 这里是猫')


class Bird(Animal):  # 子类
    def describe(self):
        print('这是继承Animal类的Bird类, 这里是鸟')

def uniform_invoke(obj):
    obj.describe()

obj_list = [Dog(), Cat(), Bird()]
for i in obj_list:
    uniform_invoke(i)

# 这是继承Animal类的Dog类, 这里是狗
# 这是继承Animal类的Cat类, 这里是猫
# 这是继承Animal类的Bird类, 这里是鸟
# 
# Process finished with exit code 0

这里需要一点想象力, 假设之后需要拓展功能代码, 直接增加bird类, 然后让如列表即可.

 

3.2 鸭子类型 duck typing

  • 鸭子类型(英语:duck typing)是动态类型的一种风格。
  • 在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由”当前方法和属性的集合”决定。
  • “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
  • “当看到一个人走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这个人就可以被称为鸭子。”
  • 在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。

 

class Animal(object):  # 创建一个基类
    def describe(self):
        print('这是Animal基类')

class Dog(Animal):
    def describe(self):
        print('这是继承Animal类的Dog类, 这里是狗')


class Cat(Animal):  # 子类
    def describe(self):
        print('这是继承Animal类的Cat类, 这里是猫')


class Bird(Animal):  # 子类
    def describe(self):
        print('这是继承Animal类的Bird类, 这里是鸟')

#### 鸭子类型特性
class Human(object):
    def describe(self):
        print('这是基类Human')

class Student(Human):
    def describe(self):
        print('这是继承Human类的学生类, 这里是学生')
####


def uniform_invoke(obj):
    obj.describe()

obj_list = [Dog(), Cat(), Bird(), Student()]
for i in obj_list:
    uniform_invoke(i)

# 这是继承Animal类的Dog类, 这里是狗
# 这是继承Animal类的Cat类, 这里是猫
# 这是继承Animal类的Bird类, 这里是鸟
# 这是继承Human类的学生类, 这里是学生
#
# Process finished with exit code 0

 

只关注Student()对象本身, 而不管其属于Animal类还是Human类.

 

4. 封装

 

4.1 前置基础知识

4.1.1 前瞻与概括

  • 实例方法,第一个参数必须要默认传实例对象,一般习惯用self。如果类调用实例方法,需要显示的传self, 也就是实例对象自己
    • def function_name(self, parm1, parm2)
  • 类方法(由@classmethod装饰的方法),可以被类或类的实例对象调用。第一个参数必须要默认传类,一般习惯用cls。
    • @classmethod
          def function_name(cls):
  • 静态方法,参数没有要求 (但可以带参数)。静态方法(由@staticmethod装饰的方法)
    • @staticmethod
          def function_name():
    • 静态方法可以通过类对象和实例对象去访问, 但是很少使用实例对象去访问.
      • 静态方法可通过类对象直接方法, 不需要额外的实例化操作,  减少代码和开销.
      • 由于静态方法主要来存放逻辑性的代码,本身和类以及实例对象没有交互. 即在静态方法中,通常不会涉及到类中方法和属性的操作.
  • 备注:
    • 如果存在相同名称的 实例属性 和 类属性, 实例属性的优先级更高.

4.1.2 类属性和实例属性

类属性, 类对象和实例对象, 都可以访问

实例属性, 只有实例对象可以访问

 

class Animal(object):
    name = "黠灵"

    def __init__(self, age):
        self.age = age

xialing = Animal(3)

print(xialing.name)     # 实例对象 访问 类属性 √
print(xialing.age)      # 实例对象 访问 实例属性 √

print(Animal.name)      # 类对象 访问 类属性 √
# print(Animal.age)     # 类对象 访问 实例属性 × # AttributeError: type object 'Animal' has no attribute 'age'

 

 

4.1.3 类方法

class Animal(object):
    name = "黠灵"  # 声明一个类属性

    @classmethod  # 运用 @classmethod 声明一个类方法
    def describe(cls):
        return cls.name

xialing = Animal()

print(xialing.describe())  # 实例对象 访问 类方法 √
print(Animal.describe())   # 类对象 访问 类方法 √

# 黠灵
# 黠灵
# Process finished with exit code 0

4.1.3.1 通过类方法修改类属性

class Animal(object):
    name = "黠灵"  # 声明一个类属性

    @classmethod  # 运用 @classmethod 声明一个类方法
    def describe(cls):
        return cls.name

    @classmethod  # 运用 @classmethod 声明一个类方法
    def describe_modified(cls, parm):
        cls.name = parm


xialing = Animal()
print(xialing.describe())  # 实例对象 访问 类方法 √ 黠灵
print(Animal.describe())   # 类对象 访问 类方法 √ 黠灵

Animal.describe_modified('狸花')
print(Animal.describe())  # 狸花

# 类方法可以修改类属性

4.1.4 静态方法

class Animal(object):
    name = "黠灵"  # 声明一个类属性

    @classmethod  # 运用 @classmethod 声明一个类方法
    def describe(cls):
        return cls.name


    @staticmethod
    def describe_again():
        return Animal.name

xialing = Animal()
print(xialing.describe())  # 实例对象 访问 静态方法 √ 黠灵 
print(Animal.describe())   # 类对象 访问 静态方法 √ 黠灵

print(xialing.describe_again())  # 实例对象 访问 静态方法 √ 黠灵 (一般不会这么做)
print(Animal.describe_again())   # 直接通过类对象 访问静态方法 √ 不需要实例化.

4.1.4.1 直接通过类对象 访问静态方法

import  time
class TimeTest:
    # def __init__(self,hour,min,second):
    #     self.hour=hour
    #     self.min = min
    #     self.second = second

    @staticmethod
    def showTime():
        return time.strftime("%H:%M:%S",time.localtime())
        pass
    pass

print(TimeTest.showTime())


# t=TimeTest(2,10,15)
# print(t.showTime()) #没有必要通过这种方式去访问 静态方法

4.1.4.2 带参数的静态方法

class Demo(object):
    
    @staticmethod
    def summation(a, b):
        print(a + b)
        
Demo.summation(1,2)

# 3
Process finished with exit code 0

 

4.2 什么是封装?

4.3 如何实现封装

4.3.1 私有属性

4.3.1.1 私有化的实例/类属性, 不能在类外部直接访问

没有使用私有属性封装的代码形式

class Animal(object):
    def __init__(self):
        self.name = "黠灵"
        self.age = "6"

xialing = Animal()
print(xialing.name, xialing.age)

使用了私有属性封装的代码形式

class Animal(object):
    def __init__(self):
        self.__name = "黠灵"      # 使用双下划线, 设置一个私有化属性__name
        self.age = "9999"

    # __str__ 将类的实例变成 str; print打印出来是个对象;使用了就把对象变成字符串
    def __str__(self):
        return f'狗名:{self.__name} | 狗命 {self.age}'

xialing = Animal()
print(xialing)
# print(xialing.name, xialing.age) # AttributeError: 'Animal' object has no attribute 'name'

 

如果不理解__str__,  可以看另一个实例


class Animal(object):
    def __init__(self):
        self.__name = "黠灵"      # 设置一个私有化属性
        self.age = "9999"

    def get_data(self):
        print(f'狗名:{self.__name} | 狗命 {self.age}')

xialing = Animal()
xialing.get_data()      # 此时在类的外部, 实例对象, 通过类内部的get_data, 间接地访问__name
print(xialing.age)      # 此时在类的外部, 实例对象, 直接访问类内部的age
#print(xialing.__name)  # 此时在类的外部, 实例变量, 直接访问类内部的私有变量 __name (报错)

 

4.3.1.2 父类中私有化的实例/类属性, 不能被子类继承

class Animal(object):
    def __init__(self):
        self.__name = "黠灵"      # 设置一个私有化属性
        self.age = "9999"

    # __str__ 将类的实例变成 str; print打印出来是个对象;使用了就把对象变成字符串
    def __str__(self):
        return f'狗名:{self.__name} | 狗命 {self.age}'

xialing = Animal()
print(xialing)
# print(xialing.name, xialing.age) # AttributeError: 'Animal' object has no attribute 'name'

class Cat(Animal):
    pass

lihua = Cat()
print(lihua.__name)  # AttributeError: 'Cat' object has no attribute '__name'
# 子类的Cat的实例化对象lihu 在类的外部, 无法访问__name 属性

 

4.3.1.3 私有化的实例/类属性, 可以在类的内部访问

以上代码已证实

 

4.3.2 私有方法

 

class Animal(object):
    def fly(self):
        print("fly")

    def climb(self):
        print('climb')


xialing = Animal()
xialing.fly(), xialing.climb() # 正常访问

 

class Animal(object):
    def __fly(self):    # 一个私有化方法
        print("fly")

    def climb(self):
        print('climb')


xialing = Animal()
xialing.fly(), xialing.climb()  # 报错

 

4.3.3 property方法

 

封装的目的不是为了完全的封闭, 而是为了更安全, 更高效的提供数据. 大多数情况下, 私有化的属性和方法, 还是要和外部进行交互. 正常情况, 可以在类的内部设计一个用于和外部交换的方法. 但除此之外, Python 还提供了一个专门的函数 property(). 直观和通俗的讲:

  • 通常访问私有属性, 需要设置两个方法, 一个修改, 一个访问.
  • 这种调用方式, “感觉”是调用一个方法, 而不是访问属性. 那么如何让调用者可以直接访问属性, 而且我们又能控制的方式 提供给调用者?
  • property
  • property 还可以和装饰器结合使用, 快速高效地实现一些功能

 

class Animal(object):
    def __init__(self):
        self.__age = 6


    def get_data(self):
        return self.__age

    def set_data(self, value):
       self.__age = value


    age = property(get_data, set_data)

    print(type(age))

xialing = Animal()
xialing.age = 999
print(xialing.age)  

语法特性

  • property 可以修饰类中属性的 get 、 set 和 del 方法,以及为属性对象创建文档字符串。
  • 原型为 class property(fget=None, fset=None, fdel=None, doc=None) ,它返回 property 属性。
  • fget 是获取属性值的函数。 fset 是用于设置属性值的函数。 fdel 是用于删除属性值的函数。 doc 为属性对象创建文档字符串。

本实例中

  • xialing.age = 999 调用 setter
  • xialing.age 调用 getter

 

4.3.4 单例模式

魔术方法 __new__

单例模式是常用和典型的”封装”形式和提现. Python里的单例模式有很多种实现方式,  比如模块, 装饰器, 类方法.  这里是通过魔术方法new.

  • 当我们实例化一个对象时,是先执行了类的__new__方法,实例化对象.
  • 然后再执行类的__init__方法,对这个对象进行初始化.
  • 那么在new 和 init之间, 可以进行判断, 如果类里面具有某个属性, 则不再创建对象.
  • 从而实现”一个类里面, 只有一个实例”.

 

class Animal(object):
    def __init__(self):
        pass


    def __new__(cls, *args, **kwargs):
        cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

xialing = Animal()
print(id(xialing))

huban = Animal()
print(id(huban))


# 1639090149520
# 1639090149648
# Process finished with exit code 0

可以看到生成两个类

 

class Animal(object):
    def __init__(self):
        pass


    def __new__(cls, *args, **kwargs):
        if not hasattr(cls,'_instance' ):
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

xialing = Animal()
print(id(xialing))

huban = Animal()
print(id(huban))


# 2642697705424
# 2642697705424
# Process finished with exit code 0

单例模式的实现

 

4.4 动态绑定

4.4.1 动态绑定实例方法

import types

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

    # def __str__(self):
    #     print(f'self.name | self.age')


def add_method(self):
    print(f'{self.name} | {self.age}| {self.color}')


xialing = Animal('黠灵', '10')

# 动态增加属性 color
xialing.color = 'yellow'

# 动态绑定add_method方法
xialing.describe = types.MethodType(add_method, xialing)

# 调用动态绑定的方法
xialing.describe()

实例方法describe

 

4.4.2 动态绑定类方法

import types

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

    # def __str__(self):
    #     print(f'self.name | self.age')


def add_method(self):
    print(f'{self.name} | {self.age}| {self.color}')

@classmethod
def add_classmethod(cls):
    print('Animal类 动态添加了 类方法describe_classmethod ')

Animal.describe_classmethod = add_classmethod
Animal.describe_classmethod()

# Animal类 动态添加了 类方法describe_classmethod 

 

4.4.3 动态绑定静态方法

import types

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

    # def __str__(self):
    #     print(f'self.name | self.age')


def add_method(self):
    print(f'{self.name} | {self.age}| {self.color}')

@staticmethod
def add_staticmethod():
    print('Animal类 动态添加了 静态方法describe_staticmethod ')

Animal.describe_staticmethod = add_staticmethod
Animal.describe_staticmethod()

# Animal类 动态添加了 静态方法describe_staticmethod 

4.4.4 限制属性动态添加 __slot__()

 

class Animal(object):
    __slots__ = ('name', 'age')
    # __slots__ = ('name') # AttributeError: 'Animal' object has no attribute 'age'


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


    def describe(self):
        print(f'{self.name} | {self.age}')

xialing = Animal('黠灵', '10')
xialing.describe()

 

 

 

本站文章除单独注明外均为原创,本文链接https://bowmanjin.com/121,未经允许请勿转载。

1

评论0

请先

没有账号? 注册  忘记密码?