python 面向对象(二)类的继承-多态-经典类和新式类-静态方法,类方法,实例方法-python中的下划线-常用魔术方法

目录

1. 类的继承

1.1  面向对象的好处

 1.2  示例

1.3  super()方法

1.4   类和实例的关系

1.4.1  isinstance ()函数

1.4.2  示例:判断类与实例的关系

2.多态

2.1   Python中的多态

2.2  Python中的多态- 注意

2.3   多态性的好处

3. 经典类和新式类

3.1. python2和python3的类型区别

3.2  类的多重继承

3.2.1   类可以多重继承

3.2.2  经典类和新式类的继承顺序

3.2.3    经典类与新式类的继承继承原理

3.2.4  C3算法

 4. 静态方法,类方法,实例方法

4.1  实例方法

4.2  类方法 @classmethod

4.3  静态方法  @staticmethod

4.4  各种方法的区别

5. python中的下划线

5.1  以单下划线开头的

5.1.1  在类里面

5.1.2  在模块里面

5.2  以双下划线开头的

5.2.1  在类里面

5.2.2  在模块里面

5.3  以双下划线开头和双下划线结尾的

6.python中常见的魔术方法

 构造函数(__new__/__init__)

 析构函数(__del__)

 调用方法(__call__)

 获取数据(__getitem__)

 删除数据(__delitem__)

 设置数据(__setitem__)

 其他魔术方法


1. 类的继承

1.1  面向对象的好处

面向对象的好处
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过 继承 机制。
继承完全可以理解成类之间的 类型和子类型 关系。
可以节省很多的代码,不需要写,直接使用
衍生出不同的子类,大部分代码一样,部分代码不一样

############################################ 

 1.2  示例

类的继承
代码:父类Parent/子类Child
子类有__ini__: 执行自己的初始化函数
子类没有__init__: 在子类实例化的时候会自动执行父类的构造函数
class Animal():
    species = "animal"
    count = 0

    def __init__(self):
        self.name = "animal"
        Animal.count += 1
        print("初始化animal...")

    def breath(self):
        print("i can breath")

    def eat(self):
        print("i can eat")


class Person(Animal):
    # 重写父类的属性 species
    species = "Person"


animal1 = Animal()
print(animal1.count)
# 初始化自己没有init,会执行父类的__init__
p = Person()
print(p.species, p.count)

 print(p.name)

Person类没有__init__,所以会调用父类Animal的__init__
因为Animal的__init__有name属性,所以不会报错
print(d.name)
而Dog类自己有__init__,就不会再调用父类的__init__
但是Dog类的__init__里面没有name属性,所以会报错
class Dog(Animal):

    def __init__(self):
        print("i am dog")

    # 重写父类eat方法
    def eat(self):
        print("dog is eating...")


d = Dog()
# # person类没有init,会调用父类init
# # 父类init有name属性,所以不会报错
# print(p.name)
# # dog类自己有init,不会再调用父类init
# # dog类init里面没有name属性,会报错
# print(d.name)
d.eat()


############################################ 

1.3  super()方法

如果一个子类的__init__方法想使用父类的__init__中的属性,这个时候可以使用super()方法

但是super()方法一般写在最前面,因为如果写在后面的话父类的__init__方法可能会覆盖子类的

一些属性。

class Pig(Animal):
    count = 0   # 重写父类属性

    def __init__(self):
        # 调用父类的init
        super().__init__()
        # super().eat()
        self.name = "pig"
        Pig.count += 1
        print("初始化pig")


print('*' * 20)
pig1 = Pig()
print(pig1.count, animal1.count)


********************
初始化animal...
初始化pig
1 3

############################################ 

1.4   类和实例的关系

1.4.1  isinstance ()函数

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

isinstance() 与 type() 区别:

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

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

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

语法

以下是 isinstance() 方法的语法:

isinstance(object, classinfo)

############################################ 

1.4.2  示例:判断类与实例的关系

# 类与实例的关系
print('*'*20)
print(isinstance(pig1, Pig), isinstance(pig1, Animal))
print(type(pig1))
a = str(10)
# 所谓的工厂函数其实本质就是类
# ’ABC‘.lower(),就是类调用lower方法
print(type(a), str.lower("ABC"), "ABC".lower())


********************
True True
<class '__main__.Pig'>
<class 'str'> abc abc

############################################ 

2.多态

2.1   Python中的多态

多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的
实现方式即为多态。
Python是一种多态语言,崇尚鸭子类型。
在程序设计中,鸭子类型是动态类型的一种风格。
鸭子模型是指:"当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就
可以被称为鸭子。"
我们并不关心对象是什么类型,到底是不是鸭子,只关心行为
class zhifubao():

    def pay(self):
        print("this is zhifubao pay")


class weixin():

    def pay(self):
        print("this is weixin pay")


class bank():

    def pay(self):
        print("this is bank pay")


z = zhifubao()
w = weixin()
b = bank()


def pay(obj):  # 接口的多种形态,接口的重用
    obj.pay()


pay(z)
pay(w)
pay(b)


E:\python\python3.9.1\python.exe E:/tlbb/2022-05-28-python面向对象/05.多态.py
this is zhifubao pay
this is weixin pay
this is bank pay

Process finished with exit code 0

############################################ 

2.2  Python中的多态- 注意

多态以继承和重写父类方法为前提
多态是调用方法的技巧,不会影响到类的内部设计
############################################ 

2.3   多态性的好处

增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
增加了程序额可扩展性
通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
多态:同一种事物的多种形态,动物分为人类,猪类(在定义角度)
多态性:一种调用方式,不同的执行效果(多态性)
实现接口重用
############################################ 

3. 经典类和新式类

3.1. python2和python3的类型区别

python2里面只有继承了object的类才是新式类,其他的是经典类

python3里面默认所有类都是继承的object,所以python3都是新式类


# 经典类 通过type查看到的实例都是 instance
#       类和实例只能通过.__class__属性
# 新式类 通过type查看到的实例类型就是类名

<class '__main__.A'> 

# __main__代表当前模块

 # 代表当前模块下的A类
[root@localhost lianxi]# python2
Python 2.7.5 (default, Oct 14 2020, 14:45:30)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class A: pass
...
>>> a = A()
>>> type(a)
<type 'instance'>
>>> a.__class__
<class __main__.A at 0x7f2a02719258>
>>>
─────────────────────────────────────────────────────────────────
[root@localhost ~]# python3
Python 3.6.8 (default, Nov 16 2020, 16:55:22)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class A: pass
...
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> a.__class__
<class '__main__.A'>
>>>

############################################ 

3.2  类的多重继承

3.2.1   类可以多重继承

定义类A
定义类B,C继承自A
定义类D,继承自D
定义类E,继承自E
定义类F,继承自F

class A():
    def test(self):
        print("from A")


class B(A):
    def test(self):
        print("from B")


class C(A):
    def test(self):
        print("from C")


class D(B):
    def test(self):
        print("from D")


class E(C):
    def test(self):
        print("from E")


class F(D, E):
    def test(self):
        print("from F")


f = F()
f.test()

经典类: F--D--B--A--E--C

新式类: F--D--B--E--C--A

############################################ 

3.2.2  经典类和新式类的继承顺序

Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是: 深度优先 广度优先

 ############################################ 

3.2.3    经典类与新式类的继承继承原理

MRO(Method Resolution Order): 方法解析顺序
MRO是在Python类的多重继承中,解决当定义了 多个同名的方法/属性 时,为避免产生歧义,保证
用户找到正确的对象所实现的一种算法。
对于你定义的每一个类,Python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一
个简单的所有基类的线性顺序列表
Python2.2以前的版本: 经典类(classic class) 时代
MRO的方法为DFS(深度优先搜索(子节点顺序:从左到右))
Python2.2版本: 新式类(new-style class)诞生 ,这时有两种MRO的方法
经典类MRO为DFS(深度优先搜索(子节点顺序:从左到右))
新式类MRO为BFS(广度优先搜索(子节点顺序:从左到右))
Python2.3到Python2.7: 经典类、新式类和平发展
从Python2.3开始新式类的MRO取而代之的是 C3算法
Python3到至今: 新式类一统江湖
Python3开始就 只存在新式类 了,采用的MRO也依旧是 C3算法
############################################ 

3.2.4  C3算法

# c3算法
# 首先将自身类加入本序列,然后再对继承序列的元素依次判断
# 若某个元素不在其他序列或者他是所有继承序列的第一个,那么就把这个元素提取到本序列

############################################  

 4. 静态方法,类方法,实例方法

class A:
    name = "class A"

    def __init__(self):  # 自动调用
        self.country = "china"

    # 实例方法,第一参数代表实例本身
    def normal_method(self, name):
        # 方法的里面,即可以访问类属性,又可以访问实例属性
        print("normal:")
        print(self.name, name)

    # 类方法 cls代表类
    @classmethod  # 装饰器 被装饰器装饰过的方法称为类方法
    def class_method(cls, name):
        # 类方法可以类属性
        print("classmethod")
        print(cls, cls.name)

    @staticmethod  # 静态方法
    def static_method(name):
        # 静态方法可以通过类名去访问类属性
        print("static_method", name, A.name)


a1 = A()
# 静态方法,实例方法,类方法都能通过实例去调用
a1.normal_method("a1")
a1.class_method("a1")
a1.static_method("a1")
# 也可以通过类去调用
# 类调用实例方法需要传入实例参数
A.normal_method(a1, "A")
A.class_method("A")
A.static_method("A")

############################################  

4.1  实例方法

实例方法,第一参数代表实例本身

在实例方法里面即可以访问类属性,又可以访问实例属性

可以通过self访问实例属性
    # 实例方法,第一参数代表实例本身
    def normal_method(self, name):
        # 方法的里面,即可以访问类属性,又可以访问实例属性
        print("normal:")
        print(self.name, name)

############################################  

4.2  类方法 @classmethod

@classmethod  --> 装饰器

被装饰器装饰过的方法称为类方法

类方法可以访问类属性

可以通过cls访问类属性(希望取的值不受实例影响时使用)
    # 类方法 cls代表类
    @classmethod  # 装饰器 被装饰器装饰过的方法称为类方法
    def class_method(cls, name):
        # 类方法可以类属性
        print("classmethod")
        print(cls, cls.name)

############################################  

4.3  静态方法  @staticmethod

静态方法可以通过类名去访问类属性

    @staticmethod  # 静态方法
    def static_method(name):
        # 静态方法可以通过类名去访问类属性
        print("static_method", name, A.name)

############################################  

4.4  各种方法的区别

各种方法的区别( @staticmethod、@classmethod
实例方法不能通过类名调用,但是静态方法和类方法是可以(不实例化访问)
实例方法:可以通过self访问实例属性
类方法:可以通过cls访问类属性(希望取的值不受实例影响时使用)
静态方法:不可以访问,通过传值的方式

############################################  

5. python中的下划线

标识符是用来标识某种对象的名称。
在命名标识符时,需要遵循一定规则。标识符的第一个字符必须是字母(大小写均可),或者是一个下划
线("_")。
以下划线开头的标识符是有特殊意义的

############################################  

5.1  以单下划线开头的

5.1.1  在类里面

这类成员变量叫做 保护变量 ,意思是只有类对象和子类对象自己能访问到这些变量

5.1.2  在模块里面

如果你写了代码“
from <模块/包名> import *”,那么以“_”开头的模块和包都不会被导 入,除非模块或包中的“
__all__”列表显式地包含了模块和包。
这有点类似于 惯例 ,为了使其他人(或你自己)使用这些代码时将会知道 以“
_”开头的名称只供内部
使用 。正如Python文档中所述:以下划线“
_”为前缀的名称(如_spam)应该被视为API中非公开的
部分(不管是函数、方法还是数据成员)。此时,应该将它们看作是一种实现细节,在修改它们时无
需对外部通知。

############################################  

5.2  以双下划线开头的

5.2.1  在类里面

类:只有类对象自己能访问,连子类对象也不能访问到这个数据。强行访问“ 对象名._类名
__xxx “这样的方式

5.2.2  在模块里面

不能模糊导入模块  即 不能用“ from xxx import *“导入包/模块。

名称(具体为一个方法名)前双下划线(“
__“)的用法并不是一种惯例,对解释器来说它有特定
的意义。 Python中的这种用法是为了避免与子类定义的名称冲突 。Python文档指出,“
__spam”
这种形式(至少两个前导下划线,最多一个后续下划线)的任何标识符将会被
_classname__spam”这种形式原文取代,在这里“
classname”是去掉前导下划线的当前类
名。
class P:
    """
    this is P
    """
    _min = 1  # 保护属性
    __max = 10  # 私有属性

    def __init__(self):
        self.name = "sc"
        self.age = 4
        self.__desc = "it"

    def __make(self):
        print("这是一个私有方法")
        print(self.__desc)

    def _protectmake(self):
        print("这是一个保护方法")

    def show(self):
        print(self.__max, self.__desc)


class Child(P):
    def show(self):
        print(self.__max)
#
#
p = P()
c = Child()

# 保护属性和普通属性没有区别
print(c._min, p._min, Child._min, P._min)
# 访问私有属性 子类访问不到私有成员
# print(c.__make)
# 类对象也不能访问私有成员
# print(p.__max)
# p.__make()
# 想访问__make()要加上类名 p._P__make()
# 私有成员只能在类的内部访问
p.show()
print('*' * 20)
print(c._min)
# print(dir(p))
# '_P__desc', '_P__make', '_P__max',
# python中的私有都是伪私有,实际其实就是将双下划线开头的标识符
# 改了一个名字存储  _类名__标识符


E:\python\python3.9.1\python.exe E:/tlbb/2022-05-28-python面向对象/08.python中的下划线.py
1 1 1 1
10 it
********************
1

Process finished with exit code 0

############################################  

5.3  以双下划线开头和双下划线结尾的

以双下划线开头和结尾的( __foo__ )
代表Python中特殊方法专用的标识。其实,这只是一种惯例,对Python系统来说,这将确保不会
与用户自定义的名称冲突。
例如__init__()代表类的构造函数。
虽然你也可以编写自己的特殊方法名,但不建议用户使用这种命名方式。
__dict__ 查看命名空间
__class__ 查看对象属于哪个类
__name__查看类的名称
__module__查看所在哪个模块
__doc__ 文档注释, 类, 函数 的文档注释都会放到__doc__里面
# 常见的以双下划线开头和以双下划线结尾的特殊变量
# __dict__  # 查看命名空间
print(P.__dict__)
print(p.__dict__)
print(P.__name__)
# __class__ 查看对象属于哪个类
print(p.__class__)
# __module__  查看所在哪个模块
print(P.__module__)
#
# __doc__文档注释, 类,函数的文档注释都会放到 __doc__里面
print(P.__doc__)



E:\python\python3.9.1\python.exe E:/tlbb/2022-05-28-python面向对象/08.python中的下划线.py
{'__module__': '__main__', '__doc__': '\n    this is P\n    ', '_min': 1, '_P__max': 10, '__init__': <function P.__init__ at 0x0000021A1689DC10>, '_P__make': <function P.__make at 0x0000021A168E1940>, '_protectmake': <function P._protectmake at 0x0000021A168E19D0>, 'show': <function P.show at 0x0000021A168E1A60>, '__dict__': <attribute '__dict__' of 'P' objects>, '__weakref__': <attribute '__weakref__' of 'P' objects>}
{'name': 'sc', 'age': 4, '_P__desc': 'it'}
P
<class '__main__.P'>
__main__

    this is P
    

Process finished with exit code 0

############################################  

6.python中常见的魔术方法

魔术方法 :一般以双下划线开头和双下划线结尾

而且是有特殊含义的方法,一般不需要手动去调用

它会在某种特定场景下自动执行

构造函数(__new__/__init__)

__new__:创建实例
__init__:初始化实例

析构函数(__del__)

在实例释放、销毁的时候自动执行的,通常用于做一些收尾工作,
如关闭一些数据库连接,关闭
打开的临时文件
class A:
    def __del__(self):
        print("this is A.del")


a1 = A()
del a1
print("xxxxxx")

调用方法(__call__)

把类实例化后的对象当做函数来调用的时候自动被调用
class A:
    def __call__(self, name):
        print(f"i am A.__call__,name is {name}")


a1 = A()
a1("sc")


def func1():
    pass


print(dir(func1))

自定义异常类

class NotIsIntException(Exception):
    def __str__(self):
        return 'NotIsIntException类型不是整数'


n = input('请输入一个整数')
try:
    if n.isdigit():
        print(n)
    else:
        raise NotIsIntException
except NotIsIntException as ni:
    print(ni)

############################################  

获取数据(__getitem__)

a[key]来访问对象,a[1:3]切片时调用__getitem__

删除数据(__delitem__)

del r[1] 时调用__delitem__方法

设置数据(__setitem__)

r['e']=213时调用__setitem__
class A:
    def __init__(self):
        self.data = {}

    def __getitem__(self, key):
        print("get data")
        return self.data.get(key, 0)

    def __setitem__(self, key, value):
        print("set data:", key, value)
        self.data[key] = value

    def __delitem__(self, key):
        print("delete data")
        del(self.data[key])


a1 = A()
print(a1["key1"])
a1["key1"] = "xxxxxxx"
del a1["key1"]

############################################  

其他魔术方法

__eq__(self, other) 定义了等号的行为, ==
__ne__(self, other) 定义了不等号的行为, !=
__lt__(self, other) 定义了小于号的行为,
<
__gt__(self, other) 定义了大于等于号的行为,
>=
__add__(self, other) +运算
__mul__(self, other) *运算
__len__(self, other) 获得长度
以加法为例:
class B:
    def __init__(self, num):
        self.num = num

    def __add__(self, x):
        print("this is add")
        return self.num + x


a = B(4)
print(a + 6)

猜你喜欢

转载自blog.csdn.net/qq_48391148/article/details/125361143
今日推荐