一木.溪桥学Python-13:多继承、多态、常用魔法方法、单例模式

本文介绍了Python中的多继承机制,如何遵循左消歧规则,以及多态的实现原理。同时深入探讨了魔法方法如`__doc__`、`__class__`等的用法,最后讲解了单例模式的实现策略和应用场景。

一木.溪桥 在Logic Education跟Amy学Python

逻辑教育 :https://logicedu.ke.qq.com

12期:Python基础课

一木.溪桥学Python-13:多继承、多态、常用魔法方法、单例模式

日期:2021年1月13日


学习目标:

  • 多继承
  • 多态
  • 常用魔法方法
  • 单例模式

学习内容:

多继承

  • 多继承,就是子类有多个父类,并且具有父类的特征。
情景1:
  • 遵循左边优先原则

  • 图:在这里插入图片描述

  • eg. :

class Father(object):
    def run(self):
        print("Father 会跑")


class Father1(object):
    def run(self):
        print("Father1 会跑")


class Son(Father, Father1):
    pass


s = Son()
s.run()		# Father 会跑 ;左边优先
情景2:
  • 左边一条路走到黑。

  • 在这里插入图片描述

  • eg. :

class GrandFather(object):
    def sleep(self):
        print("GrandFather 睡觉")


class Father(GrandFather):
    def run(self):
        print("Father 会跑")


class Father1(object):
    def run(self):
        print("Father1 会跑")

    def sleep(self):
        print("Father1 睡觉")


class Son(Father, Father1):
    pass


s = Son()
s.sleep()       # GrandFather 睡觉; 左边一条路走到底
情景3:
  • 左边优先,根最后执行。

  • 在这里插入图片描述

  • eg. :

class GrandFather(object):
    def sleep(self):
        print("GrandFather 睡觉")


class Father(GrandFather):
    def run(self):
        print("Father 会跑")


class Father1(GrandFather):
    def run(self):
        print("Father1 会跑")

    def sleep(self):
        print("Father1 睡觉")


class Son(Father, Father1):
    pass


s = Son()
s.sleep()       # Father1 睡觉 同一个根时,根最后执行
print(Son.__mro__)      # .__mro__ 是C3 算法,用于查看继承的执行顺序。
# (<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Father1'>, <class '__main__.GrandFather'>, <class 'object'>)

多态

  • 多态的概念是应用于 Java 和 C# 这一类强类型语言中,而 Python 崇尚"鸭子类型"

  • 动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,它就可以被看做是鸭子。

  • 所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。

  • Python 中 用一个函数调用多个不同的功能的函数(具有相同的函数名),实现不同的功能,就形成了多态。

  • eg. :

class Person(object):
    def print_self(self):           # 同名函数1,实现不同的功能输出
        print("自我介绍")


class Man(Person):
    def print_self(self):           # 同名函数2,实现不同的功能输出
        print("man 的自我介绍")


def print_self(obj):                # 下面用这一个函数去调用,同名函数1/2,实现不同的功能输出
    obj.print_self()                # 调用同名函数.print_self()


zs = Person()
# zs.print_self()       # 自我介绍 ,这里是常规的函数调用方法
print_self(zs)          # 自我介绍;函数引用,传参zs


hansen = Man()
# hansen.print_self()   # man 的自我介绍 ,这里是常规的函数调用方法
print_self(hansen)      # man 的自我介绍;函数引用,传参hansen

常用魔法方法

魔法方法介绍
  • 在Python中,有一些内置好的特定方法,这些方法在进行特定的操作时会自动被调用,称为魔法方法。
  • 魔法方法的命名总是被双下划线包围,比如__ 名称 __
常用魔法方法
__doc__ :用来查看类的说明文档
  • eg. :
print(list().__doc__)			# list() -> new empty list
										# list(iterable) -> new list initialized from iterable's items

li = list("abc")
print(li.__doc__)

s = "123"
print(s.__doc__)
__class__ :
__module__:
class Demo(object):
    """
    我是注释...
    """
    pass


d = Demo()
print(d.__doc__)            # 类的说明文本
print(Demo.__module__)      # __module__查当前文件所在模块;__main__ ;
print(Demo.__class__)       # __class__ 查看对象的类;Demo类是type的对象<class 'type'>
print(d.__class__)          # d是Demo的对象<class '__main__.Demo'>
__dict__ :用于获取类或者实例的属性字典
  • eg. :
class Demo(object):
    country = "中国"

    def __init__(self):
        self.name = "deng"
        self.__age = 18

    def test(self):
        self.gender = "female"
        print("test")


d = Demo()
d.test()                    # test
print(d.__dict__)           # 返回字典,对象去访问,只能访问到对象中的成员(实例属性)
print(Demo.__dict__)        # 返回字典,类名去访问,可以访问到类当中的成员(除了实例属性之外的属性与方法)

print(d.__dir__())          # 返回列表,返回所有的成员(__dict__更倾向于是__dir__()的子集)
  • 注意:
    实例属性存储在对象中,所以通过 对象 .__dict__ 获取的是实例属性
    除实例属性以外的成员都存储在类中,所以通过 类 .__dict__ 来获取
__del__() :叫做析构方法。
  • 当由该类创建的实例对象,被删除或者说在内存中被释放,将会自动触发执行。

  • 总结:
    当代码全部执行完毕才自动触发 __del__()
    如果需要提前触发,则需要通过 del关键字,删除所有对象后触发 __del__()

  • 注意:
    此方法一般不需要定义,因为 Python 是一门高级语言,程序员在使用时无需关心
    内存的分配与释放,一般都是交给 Python 解释器来执行。所以,析构函数的调用
    是由解释器在进行垃圾回收时自动触发执行。

  • eg. 1:

class Demo(object):
    def __del__(self):
        print("我被回收了")


d = Demo()
print("--"*10)
print("--"*10)

# 在所有代码都执行完毕之后,会自动的执行__del__方法

  • eg. 2:通过关键字 del 将对象删除则会主动的执行 del 方法
class Demo(object):
    def __del__(self):
        print("我被回收了")


d = Demo()
print("--"*10)
del d               # 通过关键字 del 将对象删除则会主动的执行 __del__ 方法
print("--"*10)

Run:
--------------------
我被回收了
--------------------

-eg.3 :只有当对象全部释放 才会自动触发 del

class Demo(object):
    def __del__(self):
        print("我被回收了")


d = Demo()
d1 = d
print("--"*10)
del d
# del d1			# d1 没有被释放,所以 __del__ 还未触发
print("--"*10)      # 只有当对象全部释放 才会自动触发 __del__
  • Run:
    --------------------
    --------------------
    我被回收了

  • 注意:python解释器会有自动的垃圾回收机制,所以,我们并不需要主动的去封装__del__()

__call__() :用于将对象变成一个可调用的对象。
  • 也就是说,当一个类中有__call__() 方法时,其实例化得到的对象便是可调用的 (callable)
  • eg.1 :
class Demo(object):
    def __init__(self):
        print("aaa")

    def __call__(self, *args, **kwargs):
        print("我可以调用了奥")


d = Demo()
d()         # 未用__call__的话会报错:TypeError: 'Demo' object is not callable
__new__() :用于创建与返回一个对象。
  • 在类准备将自身实例化时调用。
  • 图:选C
    在这里插入图片描述
  • eg.1:
class Demo(object):
    def __init__(self):
        print("__init__")

    def __new__(cls, *args, **kwargs):
        # 重写了父类的new方法,对象并没有创建成功,所以不会再触发__init__方法
        print("__new__")


d = Demo()
# 1.__init__创建对象之后自动调用的方法
# 2.__new__用来创建对象的

Run:
__new__

  • 注意:
    __new__() 方法用于创建对象
    __init__() 方法在对象创建的时候,自动调用
    但是此处重写了父类的 __new__() 方法,覆盖了父类__new__() 创建对象的功能,所以对象并没有创建成功。所以仅执行__new__()方法内部代码

  • 注意
    在创建对象时,一定要将对象返回,才会自动触发 __init__() 方法
    __init__() 方法当中的 self ,实际上就是 __new__ 返回的实例,也就是该对象

  • __init__() 与 new() 区别:
    __init__() 实例方法, __new__ 静态方法
    __init__() 在对象创建后自动调用, __new__ 创建对象的方法

  • eg.:

class Demo(object):
    def __init__(self):
        print("__init__")

    def __new__(cls, *args, **kwargs):
        print("__new__")

        # 重写父类的__new__,还需要执行父类的代码
        # super().__new__(cls)       # 执行了父类创建对象的代码,还并没有自动的执行__init__
        return super().__new__(cls)  # 将对象返回出去,自动执行__init__方法


d = Demo()

# Run:
# __new__
# __init__

单例模式

单例模式介绍
  • 单例模式是一种常用的软件设计模式。也就是说该类只包含一个实例。
  • 通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
  • 通常应用在一些资源管理器中,比如日志记录等。
单例模式实现
  • 思路
    当对象不存在时,创建对象
    当对象存在时,永远返回当前已经创建对象
class Single(object):
    __isinstance = None     # 1.类属性,建立标识

    def __new__(cls, *args, **kwargs):
        if cls.__isinstance is None:
            cls.__isinstance = super().__new__(cls)     # 2.将一个对象存储到__isinstance类属性中。

            # 3. 返回 cls.__isinstance, 也就是我们的对象
            return  cls.__isinstance
        else:
            # 4. __isinstance 不为None,意味着它已经存储着一个对象,我们直接将返回出去即可。
            return cls.__isinstance


s = Single()
s1 = Single()
s2 = Single()
print(id(s))             # 2378210168112
print(id(s1))            # 2378210168112
print(id(s2))            # 2378210168112

作业:

在这里插入图片描述
在这里插入图片描述


作业答案:

题1:



  • Run:

题2:



  • Run:

题3:



  • Run:

题4:



  • Run:

题5:



  • Run:

End !
Best wishes for you!

<think>好的,我现在需要帮助用户解决在Linux上安装python-geohash时由于缺少wheel包而不得不使用legacy setup.py install的问题。用户希望避免这种情况,转而使用wheel包来安装。 首先,我得回忆下用户之前的对话历史。用户之前询问过如何在Linux安装python-geohash,我给出了步骤,包括安装依赖、使用pip安装,处理导入错误等。现在用户遇到了新的问题,涉及wheel包的缺失,导致pip回退到旧的安装方法。这可能是因为python-geohash没有提供预编译的wheel,或者用户的环境中没有安装wheel包,导致pip无法生成wheel,只能使用setup.py。 根据用户提供的引用内容,特别是引用[2]和[3],问题通常出现在项目缺少pyproject.toml且没有安装wheel包时,pip会使用旧的setup.py方式。解决方法可能包括安装wheel包,强制使用PEP 517标准,或者手动构建wheel。 首先,用户需要确保系统中已经安装了wheel包。根据引用[3],安装wheel可以避免使用legacy方法。所以在步骤里,应该先让用户安装wheel。命令是pip install wheel,如果用户使用Python3,可能需要用pip3。 然后,可能需要强制pip使用PEP 517模式,这可以通过--use-pep517选项实现,如引用[2]提到的。所以安装命令应该是pip install --use-pep517 python-geohash。不过,我需要确认这个选项是否适用于当前的情况,或者是否需要其他参数。 另外,用户可能遇到的情况是,即使安装了wheel,但python-geohash本身可能没有提供合适的配置,导致pip仍然无法生成wheel。这时候可能需要用户手动构建wheel。步骤包括下载源码包,使用python setup.py bdist_wheel生成wheel文件,然后安装。但这种方法需要用户先获取源码,可能稍微复杂,但可以作为备选方案。 另外,用户可能需要升级pip本身,因为旧版本的pip可能存在兼容性问题。所以建议用户先升级pip:pip install --upgrade pip。 此外,引用[4]提到另种安装方法是用python setup.py install,但用户想避免这个,所以重点应该放在如何让pip使用wheel。 可能的步骤总结: 1. 安装wheel包。 2. 升级pip到最新版本。 3. 使用--use-pep517选项安装。 4. 如果仍然失败,手动构建wheel。 另外,需要考虑Linux环境下可能的依赖问题,比如是否安装了编译所需的工具,如gcc、python-dev等,这些在之前的回答中已经提到,用户可能已经安装过了,但可能需要再次确认,避免编译时出错。 还要检查python-geohash的PyPI页面,看看它是否提供了wheel分发。如果官方没有提供wheel,那么用户可能需要从源码构建wheel,或者等待维护者提供。但根据用户的情况,可能需要手动处理。 最后,验证安装是否成功,用import geohash测试,如之前的步骤所示。 综上,分步骤的解决方案应包括安装wheel、升级pip、使用PEP 517,以及备选的手动构建方法,并确保系统依赖已安装。</think>### 分步解决方案 #### 步骤1: 安装或更新`wheel`包 Wheel是Python的二进制包格式,可避免使用`setup.py install`的旧安装方式。 ```bash pip install --upgrade wheel # 确保已安装最新版wheel # 若使用Python3且默认命令为pip3: pip3 install --upgrade wheel ``` #### 步骤2: 强制启用PEP 517模式 通过添加`--use-pep517`参数,强制pip使用现代构建系统(避免回退到`setup.py`)[^2]: ```bash pip install python-geohash --use-pep517 ``` #### 步骤3: 安装编译依赖(备用) 若仍提示构建失败,需确保系统已安装编译工具链: ```bash sudo apt-get update sudo apt-get install python3-dev gcc # 提供Python头文件和C编译器[^1] ``` #### 步骤4: 手动构建Wheel并安装(终极方案) 如果上述方法无效,可手动下载源码并构建Wheel: ```bash # 下载源码包 pip download python-geohash --no-binary :all: tar -xzf python-geohash-*.tar.gz # 解压源码 cd python-geohash-*/ # 生成Wheel文件 python setup.py bdist_wheel # 生成.whl文件 # 安装生成的Wheel pip install dist/*.whl # 自动使用二进制包 ``` #### 验证安装 ```python import geohash print(geohash.encode(39.9, 116.4)) # 预期输出类似'wx4g' ``` --- ### 关键问题解析 1. **为何强制使用PEP 517?** PEP 517定义了无需`setup.py`的构建标准,通过`pyproject.toml`声明构建依赖,避免传统安装方式的不稳定性。 2. **手动构建Wheel的作用** 当PyPI未提供预编译Wheel时(如某些库未适配Linux平台),手动构建可生成适配当前系统的二进制包,后续安装无需重复编译[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值