语言基础篇15——Python中的面向对象编程

面向对象编程

基本组成

类和对象、属性和方法(变量与函数)

class Person:
    uuid = "123456"  # 类属性

    def __init__(self, name, age, sex):  # 方法
        self.name = name  # 对象属性
        self.age = age  # 对象属性
        self.sex = sex  # 对象属性

    def walk(self, n):  # 方法
        print(self.name + "走路" + str(n) + "步")
        
    def run(self):
        print(str(self) + "跑路")

注:使用对象概念时,有广义和狭义之分,广义上,指Python一切皆对象,类、函数或方法、变量或属性皆为对象,狭义上,特指某场景下类实例化出来的独享。如在对象实例化p = Person("张三", 15, "男")中,广义上,pPerson"张三"15均为对象,狭义上,p为对象,Person为类。

属性,attribute

属性分为类属性和对象属性,对象属性为对象独有,类属性为该类所有对象共享。Python中支持类属性和对象属性的动态创建和删除,正因如此,仅仅通过类定义无法分析类或对象的全部属性。

类属性和对象属性

# 定义类
class Person:
    uuid = "123456"

    def __init__(self, name, age, sex):  # 方法
        self.name = name
        self.age = age
        self.sex = sex

    def walk(self, n):
        print(self.name + "走路" + str(n) + "步")
        
    def run(self):
        print(str(self) + "跑路")


# 实例化对象
a, b = Person("张三", 15, "男"), Person("洛五", 17, "女")

# 类属性方访问
print(a.uuid, b.uuid, Person.uuid)  # 123456 123456 123456
Person.uuid = "654321"
print(a.uuid, b.uuid, Person.uuid)  # 654321 654321 654321

# 类属性动态添加
Person.uuid2 = "uuid2"
print(a.uuid2, b.uuid2, Person.uuid2)  #

# 类属性动态删除
del Person.uuid2
# print(Person.uuid2)  # AttributeError: type object 'Person' has no attribute 'uuid2'.

# 对象属性访问
print(a.name, b.name)  # 张三 洛五
a.name, b.name = b.name[0], a.name[0]
print(a.name, b.name)  # 洛 张

# 对象属性动态创建,uuid为对象属性
a.uuid, b.uuid = "1", "2"
print(a.uuid, b.uuid, Person.uuid)

# 对象属性动态删除,打印类属性
del a.uuid, b.uuid
print(a.uuid, b.uuid, Person.uuid)

property

property装饰器修饰类中的方法,用于定义动态属性。

  • 属性名与property装饰的函数名一致,如r,可以直接以.访问属性,如c.r
  • 获取属性r会执行r.getter修饰的方法r
  • 修改属性会执行r.setter修饰的方法r
  • 删除属性的值会执行r.setter修饰的方法r
  • 注意r名称上的一致。
  • 若不定义r.getter修饰的方法,则获取属性r时会执行property修饰的函数。
from math import pi


class Circle:

    def __init__(self):
        self.__r = 0

    @property
    def r(self):
        print("property")
        return pi

    @r.getter  # getter
    def r(self):
        print("getter")
        return self.__r

    @r.setter  # setter
    def r(self, r):
        print("setter")
        self.__r = r

    @r.deleter
    def r(self):
        print("deleter")
        self.__r = 0

    @property
    def area_property(self):
        return pi * self.r ** 2

    def area_func(self):
        return pi * self.r ** 2


c = Circle()
print(c.r)
# getter
# 0

c.r = 3
# setter

print(c.area_property)
# getter
# 28.274333882308138

print(c.area_func())
# getter
# 28.274333882308138

del c.r
# deleter

print(c.r)
# getter
# 0

方法,method

方法分为一般方法(对象方法)、类方法、静态方法。

一般方法

一般方法即对象方法,默认第一个参数为该类的实例化对象。习惯上使用对象.方法(除对象参数之外的参数列表)形式调用,调用形式直接指明对象参数,参数列表中无需手动传入,语法上支持类.方法(对象, 除对象参数之外的参数列表),调用形式未直接指明对象参数,需要手动传入对象,对象一般是该方法所属类的实例化对象,但允许传入任意对象。

class Person:
    uuid = "123456"

    def __init__(self, name, age, sex):  # 方法
        self.name = name
        self.age = age
        self.sex = sex

    def walk(self, n):
        print(self.name + "走路" + str(n) + "步")

    def run(s):  # 方法的第一个形参通常用于指向当前对象,习惯上命名为self,允许其他命名
        print(str(s) + "跑路")


def run(self):  # 习惯上,函数的形参一般不命名为self,允许该命名
    print(str(self) + "跑路")


a, b = Person("张三", 15, "男"), Person("洛五", 17, "女")

# 方法的调用
a.walk(520)  # 张三走路520步
Person.walk(a, 520)  # 张三走路520步
b.run()  # <__main__.Person object at 0x000001EE375447D0>跑路
Person.run(b)  # <__main__.Person object at 0x000001EE375447D0>跑路
Person.run("删库")  # 删库跑路,指定与Person类无关的"删库"对象

类方法

类方法是以装饰器classmethod修饰的方法,默认第一个参数为类。类方法用于执行与对象无关的操作,若操作与对象五无关,何必实例化再调用方法。习惯上,以类.方法(除类参数之外的参数列表)形式调用,调用形式直接指明类参数,参数列表中无需手动传入,语法上支持对象.方法(除类参数之外的参数列表),调用形式上并未指明类,但根据对象可以唯一确定类,同样无需手动传入类参数。

class Person:
    uuid = "123456"

    def __init__(self, name, age, sex):  # 方法
        self.name = name
        self.age = age
        self.sex = sex

    @classmethod
    def change_uuid(cls, uuid):
        cls.uuid = uuid
        print(f"{cls.__name__} {uuid}")


a = Person("张三", 15, "男")
a.change_uuid("123")  # Person 123
Person.change_uuid("456")  # Person 456

静态方法

静态方法是以装饰器classmethod修饰的方法,没有固定的对象参数或者类参数,等同于嵌套在类中但与类无关的函数,习惯上,以类.方法(参数列表)形式调用,同样支持对象.方法(除类参数之外的参数列表)

class Person:
    uuid = "123456"

    def __init__(self, name, age, sex):  # 方法
        self.name = name
        self.age = age
        self.sex = sex

    @staticmethod
    def change_uuid(uuid):
        Person.uuid = uuid
        print(f"{Person.__name__} {uuid}")


a = Person("张三", 15, "男")
a.change_uuid("123")  # Person 123
Person.change_uuid("456")  # Person 456

绑定方法与非绑定方法

绑定方法指绑定到对象或者类的方法,绑定方法参数列表第一个即为绑定的对象或者类,使用对象或者类调用方法可以自动将对象或者类传入方法,特别地,对象调用类方法会传入对象对应的类。类中一般方法都是绑定到对象的方法,可以使用classmethod装饰器将方法绑定到类,可以使用staticmethod将方法解除绑定,既不绑定对象也不绑定类。

class Person:
    uuid = "123456"

    def __init__(self, name, age, sex):  # 方法
        self.name = name
        self.age = age
        self.sex = sex

    def walk(self, n):
        print(self.name + "走路" + str(n) + "步")

    @classmethod
    def change_uuid1(cls, uuid):
        cls.uuid = uuid
        print(f"{cls.__name__} {uuid}")

    @staticmethod
    def change_uuid2(uuid):
        Person.uuid = uuid
        print(f"{Person.__name__} {uuid}")


def run(self):
    print(str(self) + "跑路")


a = Person("张三", 15, "男")
print(run)  # 
print(a.walk)  # >
print(Person.change_uuid1)  # >
print(Person.change_uuid2)  # 

访问权限控制

访问权限控制决定外部可以访问属性或方法与否,分为公有public和私有private.

  • 属性和方法默认公有访问,使用双下划线前缀标记属性或方法为私有,如__属性或方法名
  • 魔术方法(双下方法,前后对双下划线)为公有方法。
  • 私有属性或方法会被自动转换为_类__属性或方法形式的名字,且该转换是内部的,动态添加双下划线前缀属性或方法不会转换。
class A:
    __uuid = 1

    def __init__(self, a, b):
        self.a = a
        self.__b = b

    def get_b(self):
        print(f"公有方法get_b -> {self.__b}")

    def __get_b(self):
        print(f"私有方法__get_b -> {self.__b}")


o = A("a", "b")

print(o.a)  # a
# print(o.__b)  # AttributeError: 'A' object has no attribute '__b'
print(o._A__b)  # b

o.get_b()  # 公有方法get_b -> b
# o.__get_b()  # AttributeError: 'A' object has no attribute '__get_b'. Did you mean: '_A__get_b'?
o._A__get_b()  # 私有方法__get_b -> b

o.__c = "c"
print(o.__c)  # c
# print(o._A__c)  # AttributeError: 'A' object has no attribute '_A__c'. Did you mean: '_A__b'?

o.__get_c = lambda _: "c"
print(A.__dict__)
print(o.__dict__)
o.__get_c(None)
o._A__get_c(None)  # AttributeError: 'A' object has no attribute '_A__get_c'. Did you mean: '_A__get_b'?

抽象、继承、封装、多态

抽象

  • 类是对现实实体的抽象

继承

单继承

class A:
    def __init__(self):
        print("A __init__")
        self.func()

    def func(self):
        print("A func")


class B(A):
    def func(self):
        print("B func")


b = B()  # 显然使用继承的__init__方法,调用的自己的func
# A __init__
# B func

print(b.__dict__)
print(B.__dict__)
print(A.__dict__)

多继承

多继承经典情形,菱形继承

class A:
    def __init__(self):
        print("A __init__")

    def func(self):
        print("A func")


class B(A):
    def func(self):
        print("B func")


class C(A):
    def func(self):
        print("C func")


class D(B, C):
    pass


D().func()
# A __init__
# B func

实例化链

某个类实例化对象时,在初始化该类中的对象属性之前应当先初始化父类定义的对象属性,否则可能出现调用继承自父类的方法时,由于父类对象属性未初始化导致方法调用异常。

初始化对象时,会调用__init__初始化方法使用,在该方法初始化对象属性之前,使用super来调用父类的初始化方法方法,同理,初始化父类对象属性之前,也会先调用父类的父类的初始化方法,依据继承关系,初始化方法会逐层调用直到调用顶层父类初始化方法执行完毕,随后次一层执行完毕,同理逐层执行完毕返回,直到再次回到这个类的初始化方法并执行完毕,此时整个初始化过程结束。

实例化链在C++Java都有所体现,不同是的,Python中允许中断初始化链。

class A:
    def __init__(self, a):
        self.a = a

    def get_a(self):
        print(self.a)


class B(A):
    def __init__(self, a, b):
        super().__init__(a)
        self.b = b


class C(A):
    def __init__(self, c):
        self.c = c


B(1, 2).get_a()  # 1
C(1).get_a()  # AttributeError: 'C' object has no attribute 'a'

方法解析顺序,mro

Python中使用C3算法确定方法解析顺序,访问类的__mro__属性可以获得线性序列。

class O:

    def func(self):
        s = "O"
        print(s + "()")
        return s


class A(O):
    def func(self):
        s = "A"
        print(s + "(" + super().func() + ")" + "  (注意此处,D非A父类)")
        return s


class B(O):
    def func(self):
        s = "B"
        print(s + "(" + super().func() + ")")
        return s


class C(A):
    def func(self):
        s = "C"
        print(s + "(" + super().func() + ")")
        return s


class D(B):
    def func(self):
        s = "D"
        print(s + "(" + super().func() + ")")
        return s


class E(C, D):
    def func(self):
        s = "E"
        print(s + "(" + super().func() + ")")
        return s


e = E()
e.func()
# O()
# B(O)
# D(B)
# A(D)  (注意此处,D非A父类)
# C(A)
# E(C)
print(E.__mro__)
# (, , , , , , )
print(E.__base__)
# 
print(E.__bases__)
# (, )


print(issubclass(A, O))  # True  是否子类
print(issubclass(A, D))  # False
print(issubclass(E, A))  # True
print(isinstance(e, E))  # True  是否实例
print(isinstance(e, C))  # True

# https://zhuanlan.zhihu.com/p/151856162
# https://zh.wikipedia.org/wiki/C3%E7%BA%BF%E6%80%A7%E5%8C%96

封装

  • property
  • 访问权限控制

多态

python本身便是多态,没有向上转型,没有重载。

抽象类

  • 抽象类使用ABCMeta元类生成。
  • 抽象方法使用abstractmethod装饰器修饰。
  • 抽象类不能实例化。
  • 继承抽象类的一般类需要实现抽象方法。
  • 继承抽象类的抽象类无需实现抽象方法。
from abc import abstractmethod, ABCMeta


# Abstract Base Class
class A(metaclass=ABCMeta):
    @abstractmethod
    def func_1(self):
        pass


class A_1(A):
    def func_1(self):
        pass


a = A_1()

object

顶层父类,Python3中所有类默认继承object,Python2中可选择性继承object,不继承object的类称之为经典类,继承object的类称之为新式类,Python3中一切类均为新式类。

class object:
    """
    The base class of the class hierarchy.
    
    When called, it accepts no arguments and returns a new featureless
    instance that has no instance attributes and cannot be given any.
    """
    def __delattr__(self, *args, **kwargs): # real signature unknown
        """ Implement delattr(self, name). """
        pass

    def __dir__(self, *args, **kwargs): # real signature unknown
        """ Default dir() implementation. """
        pass

    def __eq__(self, *args, **kwargs): # real signature unknown
        """ Return self==value. """
        pass

    def __format__(self, *args, **kwargs): # real signature unknown
        """ Default object formatter. """
        pass

    def __getattribute__(self, *args, **kwargs): # real signature unknown
        """ Return getattr(self, name). """
        pass

    def __getstate__(self, *args, **kwargs): # real signature unknown
        """ Helper for pickle. """
        pass

    def __ge__(self, *args, **kwargs): # real signature unknown
        """ Return self>=value. """
        pass

    def __gt__(self, *args, **kwargs): # real signature unknown
        """ Return self>value. """
        pass

    def __hash__(self, *args, **kwargs): # real signature unknown
        """ Return hash(self). """
        pass

    def __init_subclass__(self, *args, **kwargs): # real signature unknown
        """
        This method is called when a class is subclassed.
        
        The default implementation does nothing. It may be
        overridden to extend subclasses.
        """
        pass

    def __init__(self): # known special case of object.__init__
        """ Initialize self.  See help(type(self)) for accurate signature. """
        pass

    def __le__(self, *args, **kwargs): # real signature unknown
        """ Return self<=value. """
        pass

    def __lt__(self, *args, **kwargs): # real signature unknown
        """ Return self
        pass

    @staticmethod # known case of __new__
    def __new__(cls, *more): # known special case of object.__new__
        """ Create and return a new object.  See help(type) for accurate signature. """
        pass

    def __ne__(self, *args, **kwargs): # real signature unknown
        """ Return self!=value. """
        pass

    def __reduce_ex__(self, *args, **kwargs): # real signature unknown
        """ Helper for pickle. """
        pass

    def __reduce__(self, *args, **kwargs): # real signature unknown
        """ Helper for pickle. """
        pass

    def __repr__(self, *args, **kwargs): # real signature unknown
        """ Return repr(self). """
        pass

    def __setattr__(self, *args, **kwargs): # real signature unknown
        """ Implement setattr(self, name, value). """
        pass

    def __sizeof__(self, *args, **kwargs): # real signature unknown
        """ Size of object in memory, in bytes. """
        pass

    def __str__(self, *args, **kwargs): # real signature unknown
        """ Return str(self). """
        pass

    @classmethod # known case
    def __subclasshook__(cls, subclass): # known special case of object.__subclasshook__
        """
        Abstract classes can override this to customize issubclass().
        
        This is invoked early on by abc.ABCMeta.__subclasscheck__().
        It should return True, False or NotImplemented.  If it returns
        NotImplemented, the normal algorithm is used.  Otherwise, it
        overrides the normal algorithm (and the outcome is cached).
        """
        pass

    __class__ = None # (!) forward: type, real value is ""
    __dict__ = {}
    __doc__ = ''
    __module__ = ''

魔术方法,magic method

魔术方法指内置的名称中带有上下划线前后缀的方法,又称双下方法,例如__init__

__new__,构造方法,用于通过类创建实例

__init__,初始化方法,实例被创建后调用

__del__,终结方法,使用del将对象引用减一,引用计数为0时终结方法会被调用

__repr__,representation,返回一个对象的正式字符串表示,通常是便于解释器理解的,在适当环境下可以基于该字符串重建对象,若不可则返回一个 <...some useful description...>结构的表示信息。在调用内置函数repr时调用。

__str__,string,返回一个对象非正式的字符串表示,通常是便于人理解的。在调用内置函数strprintformat时调用,若没有__str__实现,会调用__repr__

s = "yuzao"
print(str(s))  # yuzao
print(repr(s))  # 'yuzao'
print(eval(repr(s)))  # yuzao eval重建对象

d = [1, '2', [3]]

print(str(d))  # [1, '2', [3]]
print(repr(d))  # [1, '2', [3]]


class C:
    def __init__(self, c):
        self.c = c


c = C(1)
print(repr(c))  # <__main__.C object at 0x000001DB21E33C50>

__bytes__,返回一个bytes对象。

__format__,返回一个对象的格式化字符串,在调用内置函数format、f-string和str.format()方法时调用。

比较方法

使用关系运算符比较两个对象时会调用对应方法。

__lt__,小于

__le__,小于等于

__eq__,等于

__ne__,不等于

__gt__,大于

__ge__,大于等于

协议,protocol

PEP 544 – Protocols: Structural subtyping (static duck typing)

协议用于描述静态鸭子类型,约定有具有双下方法的类型即为同一类型。与Java中接口的概念类似。

迭代器协议

实现__iter____next__方法的类型即为迭代器。

你可能感兴趣的:(Python,python,开发语言)