Python面向对象基础

python

类的定义

类的定义的语法

class ClassName:

<statement-1>

.

.

.

<statement-N>

示例代码

class Door:

def __init__(self, number, status):

self.number = number

self.status = status

类的实例化

class Door:

def __init__(self, number, status):

self.number = number

self.status = status

door = Door(1001, "open")

door.number

door.status

  • 创建对象使用类名(__init__ 函数除第一个参数外的参数列表)

  • 创建对象的时候实际执行了 __init__函数
  • __init__ 函数并不会创建对象

函数创建及初始化的过程

  1. 首先创建对象
  2. 对象作为self参数传递给__init__函数
  3. 返回self

作用域

类变量

示例代码

In [1]: class A:

...: NAME = "A" # 类的直接下级作用域 叫做类变量

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

...: self.name = name # 关联到实例的变量 叫做实例变量

...:

In [2]: a = A("a")

In [3]: a.NAME

Out[3]: "A"

In [4]: a.name

Out[4]: "a"

In [5]: A.NAME

Out[5]: "A"

In [6]: A.name

---------------------------------------------------------------------------

AttributeError Traceback (most recent call last)

<ipython-input-6-61c1cc534250> in <module>()

----> 1 A.name

AttributeError: type object "A" has no attribute "name"

In [7]: a2 = A("a2")

In [8]: a2.NAME

Out[8]: "A"

In [9]: a2.NAME = "A2" # 给示例a2的类变量NAME赋值

In [10]: a2.NAME

Out[10]: "A2"

In [11]: a.NAME

Out[11]: "A"

In [12]: A.NAME # 类变量没有变化

Out[12]: "A"

In [13]: a2.xxx = 3

In [14]: a2.xxx # 赋值之后a2多了xxx属性

Out[14]: 3

In [15]: A.NAME = "AA" # 直接修改类的类变量

In [16]: A.NAME

Out[16]: "AA"

In [17]: a.NAME # 对应的实例的类变量也发生了改变

Out[17]: "AA"

In [18]: a2.NAME # a2的类变量在之前的赋值被覆盖了,因此改变类变量的并不会影响a2

Out[18]: "A2"

所以

  • 类变量对类和实例都可见
  • 所有实例共享类变量
  • 当给实例的类变量赋值时,相当于动态的给这个实例增加了一个属性,覆盖了类变量

属性查找顺序

  • __dict__: 实例变量的字典

  • __class__: 得到实例对应的类

  • 先查找__dict__在查找__class__

代码

In [1]: class A:

...: NAME = "A"

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

...: self.name = name

...:

In [2]: a = A("a")

In [3]: a.NAME

Out[3]: "A"

In [4]: a.__class__.NAME

Out[4]: "A"

In [5]: a.__dict__

Out[5]: {"name": "a"}

In [6]: a.__class__ # a.__class__表示实例对应的类

Out[6]: __main__.A

In [7]: a.NAME = "AA"

In [8]: a.__dict__ # 覆盖类变量之后__dict__增加了一个键值对

Out[8]: {"NAME": "AA", "name": "a"}

In [9]: a.__dict__["NAME"] = "AAA" # 可以直接修改__dict__

In [10]: a.__dict__

Out[10]: {"NAME": "AAA", "name": "a"}

In [11]: a.__class__.NAME

Out[11]: "A"

In [12]: a.__class__.__dict__

Out[12]:

mappingproxy({"NAME": "A",

"__dict__": <attribute "__dict__" of "A" objects>,

"__doc__": None,

"__init__": <function __main__.A.__init__>,

"__module__": "__main__",

"__weakref__": <attribute "__weakref__" of "A" objects>})

类装饰器

参数是一个类,并且返回一个类的函数就可以是一个类装饰器。

类装饰器通常用于给类增加属性,如果增加方法,则都是类级的方法。

代码1:给类增加属性

函数方法增加:定义set_name函数给类F增加一个NAME属性

In [1]: class F:

...: pass

...:

In [2]: def set_name(cls, name): # 给cls增加属性NAME=name

...: cls.NAME = name

...: return cls

...:

In [3]: F1 = set_name(F, "F") # 返回F本身,并且F1指向F

In [4]: F1.NAME

Out[4]: "F"

In [5]: f1 = F1()

In [6]: f1.NAME

Out[6]: "F"

In [7]: F1.__dict__

Out[7]:

mappingproxy({"NAME": "F",

"__dict__": <attribute "__dict__" of "F" objects>,

"__doc__": None,

"__module__": "__main__",

"__weakref__": <attribute "__weakref__" of "F" objects>})

In [8]: f1.__dict__

Out[8]: {}

In [9]: f1.__class__

Out[9]: __main__.F

In [10]: F.__dict__ # 本质上增加的还是类F

Out[10]:

mappingproxy({"NAME": "F",

"__dict__": <attribute "__dict__" of "F" objects>,

"__doc__": None,

"__module__": "__main__",

"__weakref__": <attribute "__weakref__" of "F" objects>})

对set_name函数进行柯里化,实现带参数的类装饰器

In [2]: def set_name(name):   # 传入参数name

...: def wrap(cls): # 装饰器是wrap

...: cls.NAME = name

...: return cls

...: return wrap

...:

In [3]: @set_name("G")

...: class G:

...: pass

...:

In [4]: G.NAME

Out[4]: "G"

In [5]: class G:

...: pass

...:

In [6]: G = set_name("G")(G) # 装饰器的函数调用方法

In [7]: G.NAME

Out[7]: "G"

代码2:给类增加方法

类装饰器get_name给类H增加一个方法__get_name__

In [1]: def get_name(cls):

...: def _get_name(self):

...: return cls.__name__

...: cls.__get_name__ = _get_name # 给cls增加__get_name__指向_get_name

...: return cls

...:

In [2]: @get_name

...: class H:

...: pass

...:

In [3]: h = H()

In [4]: h.__get_name__()

Out[4]: "H"

In [5]: H.__dict__

Out[5]:

mappingproxy({"__dict__": <attribute "__dict__" of "H" objects>,

"__doc__": None,

"__get_name__": <function __main__.get_name.<locals>._get_name>,

"__module__": "__main__",

"__weakref__": <attribute "__weakref__" of "H" objects>})

类方法/静态方法

方法的定义都是类级的,但是有的方法使用实例调用,有的方法使用类来调用

  • 类方法:当一个方法,被classmethod装饰时, 第一个参数会变成类本身, 这样的方法叫类方法
  • 当一个方法, 被staticmethod装饰的时候,不会自动传递第一个参数, 这样的方法叫静态方法

代码

class I:

def print(self): # 实例方法

print("instance method")

@classmethod

def class_print(cls): # 类方法

print(id(cls))

print("class method")

@staticmethod

def static_print(): # 静态方法

print("static method")

def xxx_print(): # 一个普通方法

print("this is a function")

  • 实例方法只能由实例调用
  • 类方法可以被类和实例使用,并且被实例使用时,传入的第一个参数还是类
  • 静态方法可以被类和实例使用,都不会传入第一个参数
  • 类中的普通方法,因为没有传入self,因此只能被类使用,实例无法使用
  • 各种方法根据首参来决定。

访问控制

双下划线

  • 所有双下划线开始,非双下划线结尾的成员,都是私有成员
  • 严格的说, Python里没有真正私有成员
  • Python的私有成员是通过改名实现的:_类名 + 带双下划綫的属性
  • 除非真的有必要,并且清除明白的知道会有什么后果,否则不要通过改名规则修改私有成员

In [1]: class Door:

...: def __init__(self, number, status):

...: self.number = number

...: self.__status = status # 双下划线开始, 非双下划綫结尾的都是私有的, 在类外部无法访问

...: def open(self):

...: self.__status = "opening"

...: def close(self):

...: self.__status = "closed"

...: def status(self):

...: return self.__status

...: def __set_number(self, number): # # 双下滑先开始, 非双下划线结尾的方法也是私有方法

...: self.number = number

...:

In [2]: door = Door(1001, "closed")

In [3]: door.__status # 无法访问私有属性

---------------------------------------------------------------------------

AttributeError Traceback (most recent call last)

<ipython-input-3-d55234f04e7f> in <module>()

----> 1 door.__status

AttributeError: "Door" object has no attribute "__status"

In [4]: door.__dict__ # door对象含有的属性_Door__status

Out[4]: {"_Door__status": "closed", "number": 1001}

In [5]: door.__status = "hahaha" # 给对象创建了新的属性,并没有修改到__status

In [6]: door.__status

Out[6]: "hahaha"

In [7]: door.__dict__

Out[7]: {"_Door__status": "closed", "__status": "hahaha", "number": 1001}

In [8]: door.status()

Out[8]: "closed"

In [9]: door.open()

In [10]: door.status()

Out[10]: "opening"

In [11]: door.__set_number(1002)

---------------------------------------------------------------------------

AttributeError Traceback (most recent call last)

<ipython-input-11-888a73f63746> in <module>()

----> 1 door.__set_number(1002)

AttributeError: "Door" object has no attribute "__set_number"

In [12]: door._Door__status

Out[12]: "opening"

In [13]: door._Door__status = "hehehe" # _类名 + 带双下划綫的属性的方式直接修改私有成员

In [14]: door.status()

Out[14]: "hehehe"

单下划线

  • 单下划线是一种惯用法, 人为标记此成员为私有, 但是解释器不不做任何处理

In [1]: class A:

...: def __init__(self):

...: self._a = 3

...:

In [2]: a = A()

In [3]: a._a

Out[3]: 3

In [4]: a._a = 4

In [5]: a._a

Out[5]: 4

In [6]: a.__dict__

Out[6]: {"_a": 4}

property装饰器

引入property装饰器

class Door:

def __init__(self, number):

self.__number = number

def get_number(self):

return self.__number

def set_number(self, number):

self.__number = number

当把number属性变成私有属性__number之后,无法直接访问得到,只能通过get_numberset_number两个函数访问__number属性。

如果既能限制参数访问,又可以用类似属性这样简单的方式来访问类的变量,这个时候就可以使用property装饰器了。

  • Python内置的@property装饰器就是负责把一个方法变成属性调用的

property装饰器使用

class Door:

def __init__(self, number):

self.__number = number

# property 装饰器会把一个仅有self参数的函数,变成一个属性, 属性的值,为方法的返回值

@property

def number(self):

return self.__number

# property setter 装饰器, 可以把一个方法转化为对此赋值,但此方法有一定要求

# 1.同名 2.必须接收两个参数 self 和 value, value为所赋的值

@number.setter

def number(self, number):

self.__number = number

@number.deleter

def number(self):

print("cannot remove number property")

door = Door(1001)

door.number # 返回1001

door.number = 1002

door.number # 返回1002

del door.number # 输出cannot remove number property

继承

单继承

  • 在类名后加括号 括号中是继承列表, 称之为父类或者基类或者超类
  • 继承一个明显的好处就是可以获取父类的属性和方法

class Base:

PUBLIC_CLASS_VAR = "PUBLIC_CLASS_VAR"

__PRIVATE_CLASS_VAR = "PRIVATE_CLASS_VAR"

def __init__(self):

self.public_instance_var = "public_instance_var"

self.__private_instance_var = "private__instance_var"

@classmethod

def public_class_method(cls):

return "public_class_method"

@classmethod

def __private_class_method(cls):

return "private_class_method"

@staticmethod

def public_static_method():

return "public static method"

@staticmethod

def __private_static_method():

return "private static method"

def public_instance_method(self):

return "public_instance_method"

def __private_instance_method(self):

return "private_instance_method"

class Sub(Base):

pass

sub = Sub()

sub.__dict__

# 输出

{"_Base__private_instance_var": "private__instance_var",

"public_instance_var": "public_instance_var"}

  • 凡是公有的都能继承
  • 凡是私有的都不能继承
  • 原来是什么,继承过来还是什么

方法重写

  • 当子类和父类有同名成员的时候, 子类的成员会覆盖父类的同名成员
  • 当父类含有一个带参数的初始化方法的时候,子类一定需要一个初始化方法,并且在初始化方法中调用父类的初始化方法
  • super方法:super(type, obj) =》type:类名,obj:传递给后续方法的第一个参数

class Base:

def __init__(self):

self.__a = 4

def print(self):

print("Base.print")

@classmethod

def cls_print(cls):

print("Base.cls_print")

class Sub(Base):

def print(self): ## 当子类和父类有同名成员的时候, 子类的成员会覆盖父类的同名成员

print("Sub.print")

@classmethod

def cls_print(cls):

print("Sub.cls_print")

def foo(self):

# 调用父类的print

super().print()

# super(Sub, self).print()

@classmethod

def cls_foo(cls):

#cls.cls_print()

#Base.cls_print()

super().cls_print()

class SubSub(Sub):

def print(self):

print("SubSub.print")

@classmethod

def cls_print(cls):

print("SubSub.cls_print")

def foo(self):

# 调用Base的print

super(SubSub, self).print()

# 代理 TYPE 的父类的方法, 并且使用 obj 绑定 第一个参数 指定调用谁的直接父类, 第二个参数指定当调用时,传递什么作为方法的第一个参数

super(Sub, self).print()

super(SubSub, SubSub).cls_print() # 类方法的时候可以传递类,也可以传递实例self

@classmethod

def cls_foo(cls):

# Base.cls_print()

super(Sub, cls).cls_print()

多继承与MRO

等效的类定义

class A:

pass

class A(object):

pass

class A():

passs

多继承

  • 在继承列表里存在多个类的时候表示多继承
  • 多继承会把继承列表里的所有公有成员都继承过来

class A:

def method(self):

print("method of A")

class B:

def method(self):

print("method of B")

class C(A, B):

pass

c = C()

c.method() # 输出method of A

MRO

定义一个多继承,如下

class A:

def method(self):

print("method of A")

class B:

def method(self):

print("method of B")

class C(A, B):

pass

class E(A):

def method(self):

print("method of E")

class F(E, A):

pass

F().method() # 输出method of E

如果定义类G继承自(A, E),如下

class G(A, E):  # 在定义的时候会直接报错

pass

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

<ipython-input-51-dcac33a3d00c> in <module>()

----> 1 class G(A, E):

2 pass

TypeError: Cannot create a consistent method resolution

order (MRO) for bases E, A

报错显示:Cannot create a consistent method resolution order (MRO) for bases E, A

方法解析顺序(MRO)不满足报错

分析基类E,A的MRO

>>> A.__mro__

(__main__.A, object)

>>> E.__mro__

(__main__.E, __main__.A, object)

>>> F.__mro__

(__main__.F, __main__.E, __main__.A, object)

所以,mro序列就是继承的先后顺序

那么G类的mro序列应该就是(G, A, E, object),Python通过C3算法来确定多继承的时候是否满足mro的两个原则:

  1. 本地优先: 自己定义或重写的方法优先,按照继承列表,从左到右查找
  2. 单调性:所有子类,也要满足查找顺序

C3算法的主要作用是:在多继承时判断属性来自于哪个类,无法判断时抛出TypeError

C3算法

class B(O) :则B的mro序列为: [B, O]

class B(A1, A2, ..., An) :则B的mro序列为: [B] + merge(mro(A1), mro(A2), ..., mro(An), [A1, A2, ..., An, O])

merge操作就是C3算法的核心,merge步骤如下:

* 遍历列表

* 看第一个列表的首元素

* 它在其他列表中也是首元素

* 或者它在其他列表不存在

* 满足以上条件,则移除该首元素,合并到mro中

* 不满足,则抛出异常

C3算法分析F类的mro

mro(F) -> [F] + merge(mro(E), mro(A), [E, A, O])

-> [F] + merge([E, A, O], [A, O], [E, A, O])

-> [F, E] + merge([A, O], [A, O], [A, O])

-> [F, E, A] + merge([O], [O], [O])

-> [F, E, A, O]

merge操作成功,mro解析正确,最终mro为[F, E, A, O]

C3算法分析G类的mro

mro(G) -> [G] + merge(mro(A), mro(E), [A, E, O])

-> [G] + merge([A, O], [E, A, O], [A, E, O])

-> raise TypeError:

第一个列表的首元素为A,在第二个列表中存在但不是首元素,不满足merge的条件,直接抛出异常。

结论:

  1. 应该尽量避免多继承
  2. 多继承会对程序的心智负担造成非常大的压力

Mixin类

参考

  1. 廖雪峰-多重继承与MixIn
  2. 知乎-Mixin是什么概念?
  3. Python Cookbook-利用Mixins扩展类功能

在编程中,mixin是指为继承自它的class提供额外的功能, 但它自身却是不单独使用的类.。在具有多继承能力的编程语言中, mixin可以为类增加额外功能或方法。

因此,MixIn模式的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。

Python 3.5.2 源代码 socketserver.py 中的639到643行可以看到以下四个类的定义

class ForkingUDPServer(ForkingMixIn, UDPServer): pass

class ForkingTCPServer(ForkingMixIn, TCPServer): pass

class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass

class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

  • BaseServer:server类的基类
  • UDPServer:UDP server class,继承自BaseServer
  • TCPServer:TCP server class,继承自BaseServer
  • ForkingMixIn:Mix-in class to handle each request in a new process.
  • ThreadingMixIn:Mix-in class to handle each request in a new thread.

Python自带了TCPServerUDPServer这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixInThreadingMixIn提供。通过组合,就可以得到以上四个类。

这几个类之间的关系如下图:

可以看到,从BaseServer开始逐层继承的过程中,混入(MixIn)了ForkingMixIn类和ThreadingMixIn类。

这样的多重继承的技巧称为MixIn。

如果不采用MixIn技术,而是采用层次复杂的单继承实现,则类的数量会呈指数增长。

具体不采用MixIn技术设计的继承层次关系参见:廖雪峰-多重继承与MixIn中的Animal类的设计思路。

MixIn总结

MixIn其实也是一种组合的方式。通常来说,组合优于继承

Mixin 类的限制

  • Mixin类不应该有初始化方法
  • Mixin类通常不能独立工作
  • Mixin类的祖先也应该是Mixin类

通常情况下,Mixin类总在继承列表的第一位

记得帮我点赞哦!

精心整理了计算机各个方向的从入门、进阶、实战的视频课程和电子书,按照目录合理分类,总能找到你需要的学习资料,还在等什么?快去关注下载吧!!!

念念不忘,必有回响,小伙伴们帮我点个赞吧,非常感谢。

我是职场亮哥,YY高级软件工程师、四年工作经验,拒绝咸鱼争当龙头的斜杠程序员。

听我说,进步多,程序人生一把梭

如果有幸能帮到你,请帮我点个【赞】,给个关注,如果能顺带评论给个鼓励,将不胜感激。

职场亮哥文章列表:更多文章

本人所有文章、回答都与版权保护平台有合作,著作权归职场亮哥所有,未经授权,转载必究!

以上是 Python面向对象基础 的全部内容, 来源链接: utcz.com/z/529964.html

回到顶部