Python面向对象——Mixin机制、重载、多态与鸭子类型、绑定与非绑定方法、Python常见的内置函数

内容回顾

1、继承
    优点:解决类与类之间代码冗余问题
    缺点:将类耦合到了一起


2、python支持多继承
    优点:最大程度地重用父类的属性
    缺点:
        1、违背人的思维习惯:继承表达的是一种什么"是"什么的关系
        2、代码可读性会变差
        3、不建议使用多继承,有可能会引发可恶的菱形问题,扩展性变差,
        如果真的涉及到一个子类不可避免地要重用多个父类的属性,应该使用Mixins
    class A(object): # A.mro()
        pass

    class B(A): # B.mro()
        pass

    class C(B) # C.mro()
        pass

    obj=B()
    B.xxx
    obj.xxx

2、派生:子类中衍生出的新东西
    1、子类独有,父类没有
    2、子类有,父类也有,子类是完全覆盖父类的
    3、子类有,父类也有,子类是在父类的基础上进行拓展


    ps:在子类派生的新方法中如何重用父类的功能
    class A(object):
        def f1(self):
            pass

    class B(A):
        def f1(self):
            A.f1(self)
            拓展的代码

Mixin机制

1.什么是Mixin

在面向对象编程中,Mixin是一种类,这种类包含了其他类要使用的方法,但不必充当其他类的父类。其他类是如何获取Mixin中的方法因语言的不同而不同。所以有时候Mixin被描述为’include’(包含)而不是 inheritance(继承)。
Mixins鼓励代码重用,并且可以用于避免多重继承可能导致(“如钻石问题”)的继承歧义,或者解决一个缺乏对一种语言的多重继承的支持。mixin也可以被看作 实现方法 的接口。 这种模式是强制依赖性反转原理的一个例子。

2.Mixin来源

mix-in是一种冰淇淋,提供一些基础口味(香草,巧克力等),在这种基础口味上可以添加其他食品(坚果,小饼干)。Mixin术语由此得来

3.定义及优点

Mixins是一种语言概念,允许程序员将一些代码注入到一个类中。Mixin编程是一种软件开发的风格,其中功能单元在一个类中创建,然后与其他类混合。
mixin类扮演父类的角色,包含其他类想要的功能。 然后,一个子类可以继承或简单地重用此功能,但不能作为专业化的手段。通常,mixin将会将所需的功能导出到子类中,而不会创建一个单一的“is a”关系。 这是mixins和继承的概念之间的重要区别,因为子类仍然可以继承父类的所有功能,但是,不必一定应用关于子对象“作为一种”父语义的语义。
优点

  1. 通过允许多个类使用通用功能,但没有多重继承的复杂语义,为多重继承提供了一种机制. 2. 代码可重用性:当程序员希望在不同类之间共享功能时,Mixins很有用。 而不是一遍又一遍地重复相同的代码,通用功能可以简单地分组成一个混合,然后继承到需要它的每个类中。 3. Mixins允许继承和使用只有父类的所需功能,不一定是父类的所有功能.

4.在python中的应用

在Python中,SocketServer模块]具有UDPServer类和TCPServer类。它们分别作为UDP和TCP套接字服务器的服务器。 另外,还有两个mixin类:ForkingMixIn和ThreadingMixIn。通常,所有新连接都在相同的过程中处理。 通过使用ThreadingMixIn扩展TCPServer,如下所示:

class ThreadingTCPServer(ThreadingMixIn, TCPServer):
  pass

ThreadingMixIn类向TCP服务器添加功能,以便每个新连接创建一个新的线程。或者,使用ForkingMixIn将导致为每个新连接分叉进程。 显然,创建新线程或分支进程的功能作为独立类不是非常有用的。
在此使用示例中,mixins提供了替代底层功能,而不影响套接字服务器的功能。

5.在Django项目中的应用

Django TemplateView的实现代码如下:

class TemplateView(TemplateResponseMixin, ContextMixin, View):
    """
    A view that renders a template.  This view will also pass into the context
    any keyword arguments passed by the URLconf.
    """
    def get(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
        return self.render_to_response(context)

可以看到,这个视图‘继承了’TempalteResponseMixin,和ContextMixin以及View。TempalteResponseMixin和ContextMixin实现分别如下:

class TemplateResponseMixin(object):
    """
    A mixin that can be used to render a template.
    """
    template_name = None
    template_engine = None
    response_class = TemplateResponse
    content_type = None

    def render_to_response(self, context, **response_kwargs):
        """
        Returns a response, using the `response_class` for this
        view, with a template rendered with the given context.

        If any keyword arguments are provided, they will be
        passed to the constructor of the response class.
        """
        response_kwargs.setdefault('content_type', self.content_type)
        return self.response_class(
            request=self.request,
            template=self.get_template_names(),
            context=context,
            using=self.template_engine,
            **response_kwargs
        )

    def get_template_names(self):
        """
        Returns a list of template names to be used for the request. Must return
        a list. May not be called if render_to_response is overridden.
        """
        if self.template_name is None:
            raise ImproperlyConfigured(
                "TemplateResponseMixin requires either a definition of "
                "'template_name' or an implementation of 'get_template_names()'")
        else:
            return [self.template_name]

class ContextMixin(object):
    """
    A default context mixin that passes the keyword arguments received by
    get_context_data as the template context.
    """

    def get_context_data(self, **kwargs):
        if 'view' not in kwargs:
            kwargs['view'] = self
        return kwargs

可以看到这两个Mixin实际上只是为了给TemplatesView提供get_context_data和render_to_response两个接口以及其他需要的数据,但这里并不适合把这两个类解释为TemplatesView的父类,而更倾向于解释为提供接口及数据。把这两个类封装成mixin格式可以提高代码的重用性,把这两个类直接设计成函数我认为(个人见解,还 请指正)也是可以的,只不过这样以来就不那么面向对象编程了。所以可以说Mixin应该是一种设计思想,并没有什么独特之处。至于好多类名字中为什么要加上Mixin,这只是为了提高代码的可读性而已。

重载(在子类派生的新方法中如何重用父类的功能)

方式一:指名道姓调用某一个类下的函数 =》不依赖于继承关系

class OldboyPeople:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def f1(self):
        print('%s say hello' % self.name)


class Teacher(OldboyPeople):
    def __init__(self, name, age, sex, level, salary):
        OldboyPeople.__init__(self, name, age, sex)

        self.level = level
        self.salary = salary


tea_obj = Teacher('egon', 18, 'male', 10, 3000)
print(tea_obj.__dict__)

方式二:super()调用父类提供给自己的方法 =》严格依赖继承关系

调用super()会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro, 去当前类的父类中找属性

class OldboyPeople:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def f1(self):
        print('%s say hello' % self.name)


class Teacher(OldboyPeople):
    def __init__(self, name, age, sex, level, salary):
        # super(Teacher,self).__init__(name,age,sex)
        super().__init__(name, age, sex)  # 调用的是方法,自动传入对象

        self.level = level
        self.salary = salary


# print(Teacher.mro())
tea_obj = Teacher('egon', 18, 'male', 10, 3000)
print(tea_obj.__dict__)

super()案例

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


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


class C(A, B):
    pass


obj = C()
obj.test()

print(C.mro())


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


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


class C(A, B):
    def test1(self):
        print('from C')


obj = C()
obj.test()

print(C.mro())

多态

1、什么多态:同一事物有多种形态

class Animal:
    pass


class People(Animal):
    pass


class Dog(Animal):
    pass


class Pig(Animal):
    pass

2、为何要有多态 =》多态会带来什么样的特性,多态性

多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象

class Animal:  # 统一所有子类的方法
    def say(self):
        print('动物基本的发声频率。。。', end=' ')


class People(Animal):
    def say(self):
        super().say()
        print('嘤嘤嘤嘤嘤嘤嘤')


class Dog(Animal):
    def say(self):
        super().say()
        print('汪汪汪')


class Pig(Animal):
    def say(self):
        super().say()
        print('哼哼哼')


obj1 = People()
obj2 = Dog()
obj3 = Pig()


obj1.say()
obj2.say()
obj3.say()

定义统一的接口,接收传入的动物对象

def animal_say(animal):
    animal.say()


animal_say(obj1)
animal_say(obj2)
animal_say(obj3)


print('hello'.__len__())
print([1, 2, 3].__len__())
print({'a': 1, 'b': 2}.__len__())


def my_len(val):
    return val.__len__()


print(my_len('hello'))
print(my_len([1, 2, 3]))
print(my_len({'a': 1, 'b': 2}))

len('hello')
len([1, 2, 3])
len({'a': 1, 'b': 2})

鸭子类型

python推崇的是鸭子类型 这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述: “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

class Cpu:
    def read(self):
        print('cpu read')

    def write(self):
        print('cpu write')


class Mem:
    def read(self):
        print('mem read')

    def write(self):
        print('mem write')


class Txt:
    def read(self):
        print('txt read')

    def write(self):
        print('txt write')


obj1 = Cpu()
obj2 = Mem()
obj3 = Txt()

obj1.read()
obj1.write()

obj2.read()
obj2.write()

obj3.read()
obj3.write()

了解:

class Animal(metaclass=abc.ABCMeta):  # 统一所有子类的标准
    @abc.abstractmethod
    def say(self):
        pass

# obj=Animal() # 不能实例化抽象类自己


class People(Animal):
    def say(self):
        pass


class Dog(Animal):
    def say(self):
        pass


class Pig(Animal):
    def say(self):
        pass


obj1 = People()
obj2 = Dog()
obj3 = Pig()

绑定方法和非绑定方法

类中定义的函数分为两大类:绑定方法和非绑定方法 其中绑定方法又分为绑定到对象的对象方法和绑定到类的类方法。 1、绑定给对象的方法:调用者是对象,自动传入的是对象 2、绑定给类的方法:调用者类,自动传入的是类
在类中正常定义的函数默认是绑定到对象的,而为某个函数加上装饰器@classmethod后,该函数就绑定到了类。

一:绑定方法

特殊之处在于将调用者本身当做第一个参数自动传入

class Mysql:
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port

    def func(self):
        print('%s:%s' % (self.ip, self.port))

    @classmethod  # 将下面的函数装饰成绑定给类的方法
    def from_conf(cls):
        print(cls)
        return cls(settings.IP, settings.PORT)

# obj1=Mysql('1.1.1.1',3306)


obj2 = Mysql.from_conf()
print(obj2.__dict__)

二:非绑定方法(静态方法)

没有绑定给任何人:调用者可以是类、对象,没有自动传参的效果。该方法不与类或对象绑定,类与对象都可以来调用它,但它就是一个普通函数而已,因而没有自动传值那么一说

class Mysql:
    def __init__(self, ip, port):
        self.nid = self.create_id()
        self.ip = ip
        self.port = port

    @staticmethod  # 将下述函数装饰成一个静态方法
    def create_id():
        import uuid
        return uuid.uuid4()

    @classmethod
    def f1(cls):
        pass

    def f2(self):
        pass


obj1 = Mysql('1.1.1.1', 3306)

print(Mysql.create_id)
print(obj1.create_id)

Mysql.create_id(1, 2, 3)
obj1.create_id(4, 5, 6)

print(Mysql.create_id)
print(Mysql.f1)
print(obj1.f2)

内置函数

Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类,这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发。

print(abs(-1))
print(all([1, 'aaa', '1']))
print(all([]))

print(any([0, None, 1]))
print(any([]))

print(bin(11))
print(oct(11))
print(hex(11))

print(bool(''))


def func():
    pass


class Foo:
    pass


print(callable(Foo))  # 方

print(chr(65))
print(ord('A'))

不可变集合

s = frozenset({1, 2, 3})

hash(不可变类型)

print(round(1.5))
print(round(1.4))


10 ** 2 % 3
print(pow(10, 2, 3))
s = slice(1, 4, 2)
l1 = ['a', 'b', 'c', 'd', 'e']
l2 = ['aaa', 'bbb', 'ccc', 'ddd', 444]

print(l1[1:4:2])  # l1[s]
print(l2[1:4:2])  # l2[s]

v1 = 'hello'
v2 = [111, 222, 333, 444, 5555, 6666]
res = zip(v1, v2)
print(list(res))

divmod() 函数返回当参数 1 除以参数 2 时包含商和余数的元组。

print(divmod(10000, 33))

dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。

class Foo:
    pass

obj = Foo()
obj.xxx = 1111
print(dir(obj))  # obj.哪些属性

enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

for i, v in enumerate(['a', 'b', 'c']):
    print(i, v)

eval eval是Python的一个内置函数,这个函数的作用是,返回传入字符串的表达式的结果。即变量赋值时,等号右边的表示是写成字符串的格式,返回值就是这个表达式的结果。

res = eval('{"a":1}')  # 执行字符串中的表达式
print(res, type(res)) # {'a': 1} <class 'dict'>

isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。isinstance() 与 type() 区别

  • type() 不会认为子类是一种父类类型,不考虑继承关系。
  • isinstance() 会认为子类是一种父类类型,考虑继承关系。

如果要判断两个类型是否相同推荐使用 isinstance()。

class Foo:
    pass


obj = Foo()
print(isinstance(obj, Foo))
print(isinstance([], list))  # 类型判断推荐使用isinstance
print(type([]) is list)  # 不推荐使用

import() 函数用于动态加载类和函数 。
如果一个模块经常变化就可以使用 import() 来动态载入。

time = __import__('time')
time.sleep(3)
posted @   喝茶看狗叫  阅读(138)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示