Python 面向对象(一)

1、类和对象

1.1 基本概念

类: 用来描述具有相同的属性和方法的对象的集合—>车类
对象: 通过类定义的数据结构实例—>我的车
属性: 对象的描述信息---->车的零部件
方法: 对象的行为—>车的功能(将零部件组装,实现某种功能)

1.2 常见的三种编程范式

  • 函数式编程
    高阶函数、返回函数、装饰器等
  • 面向过程编程(函数)
    着重于作什么
    函数封装,实现复用
  • 面向对象编程(类)
    着重于谁去做
    把 变量和函数 再做1层封装
    ·在类中对数据的赋值、内部调用对外部用户是透明的。
    ·把一些功能的实现细节不对外暴露

1.3 为什么要面向对象

方便版本迭代更新, 程序结构清晰明了

  • 代码重用,实现这种重用的方法之一是通过继承机制
  • 可以节省很多的代码,不需要写,直接使用
  • 衍生出不同的子类,大部分代码一样,部分代码不一样

1.4 类的基本特征

封装

隐藏对象的属性和实现细节,仅对外提供公共访问方式。
在python中用双下划线开头的方式将属性设置成私有的。

好处:1. 将变化隔离;2. 便于使用;3. 提高复用性;4. 提高安全性。

继承

代码的重用

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 = 'Person' # 重写了父类的属性


class Dog(Animal):
    def __init__(self):
        print("i am dog")

    def eat(self):
        print("dog is eating")


class Pig(Animal):
    count = 0

    def __init__(self):
        super().__init__()  # 子类访问父类的init方法,建议放在最前面,否则跟父类的属性冲突了会覆盖
        self.name = "pig"
        Pig.count += 1
        print("初始化pig。。。。")


print("实例化Animal".center(50, '*'))
animal = Animal()
print(animal.count)
print("实例化Person".center(50, '*'))
person = Person()
print(person.count, person.species, Person.count)
# 对象的属性查找,先去对象实例空间查找,没有就去类空间查找
# 类空间没有就去父类空间查找,层层递归往上查找

print("实例化dog".center(50, '*'))
d = Dog()
d.eat()

print("实例化pig".center(50, '*'))
pig = Pig()
print(Animal.count, pig.count, pig.name)

多态

实现接口的重用

python里不支持多态,python处处是多态
不支持多态是语法上的多态,不需要额外的实现多态的代码
按照多态语法来说,不属于多态(父类作为参数,传递子类对象)
python 里处处是多态,本身实现了多态

class Alipay():
    def pay(self):
        print("支付宝的pay")
        
class WeChat():
    def pay(self):
        print("微信的pay")

class WangYi():
    def pay(self):
        print("WangYi的pay")


zhi = Alipay()
wei = WeChat(
wang = WangYi() 

def paymethod(obj):  # 不关心是什么类,只关心有没有pay()这个行为 
    obj.pay()

paymethod(wang)
paymethod(zhi)

2、类的定义和使用

Python 面向对象(一)_第1张图片

类空间和实例空间

类创建的时候会生成类空间
实例化对象的时候会生成实例空间,不同的实例空间都是独立的
实例查找属性方法的时候,先在自己的实例空间找,找不到就去类空间查找
类空间找不到就去父类找,一层一层往上找

为什么实例可以访问类属性

创建实例的时候,会有一个类对象指针,通过这个指针,实例就能访问类的属性和方法了

3、__ init__方法

初始化实例的方法 (静态方法)
实例对象的构造方法(初始化方法)
实例化对象的时候会自动调用__init__ 方法

4、__ new__ 方法

创建实例的方法(实例方法)
没有的话默认会继承object类的new方法,一般不需要重写

总结:
__ new__是创建实例的方法
__ init__ 对创建的实例进行初始化工作的方法

__ new__方法必须要传入一个参数(cls),代表当前类
__ init__ 必须要返回一个实例化对象
__ init__ 的self就表示__new__方法返回的实例,__ init__ 就对这个实例进行初始化

子类没有定于__ new__ 就会去找父类的__ new__
新式类才有 __ new__
如果实例化对象和本身class不一致,__ init__就不会执行

单例模式

无论实例化多少次,都只会返回同一个实例对象
通过重写new方法实现

class Student:
    obj = None
    def __new__(cls, *args, **kwargs):
        if cls.obj:
            cls = object.__new__(cls)
        return cls.obj

a = Student()
b = Student()
c = Student()
print(id(a), id(b), id(c))

5、self详解

self代表类的实例,而非类
实例方法里面,第一个参数就代表实例本身,所以不一定非写成self
如果要通过实例调用,第一个参数必须要传,因为实例调用底层实现是Person.info( p ), p是必传参数
可以Peson.info()方法调用

class Person:
    name = "hejin"
    def info(self):
        print(f'i am {self.name}')
        print(self)
        print(type(self))

p = Person() 
p.info()  # 底层解释器 Person.info(p),因为实例空间是没有info方法,
# 执行类里面的Person.info(p)是这里的p 就代表实例本身

6、类和实例

关系判断

print(isinstance(person,Person),isinstance(person,Animal)) #True True  判断是否是类的实例
print(type(person)) #  只能判断当前类
print(Pig.__base__) #  查找父类

6.1 使用类实现迭代器

class Iterator1():
    def __init__(self, it):
        self.index = 0
        self.it = it

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.it):
            print("迭代结束")
        else:
            self.index += 1
            return self.it[self.index - 1]
 
lst = [1, 2, 3, 4, 5, 6]
str1 = "abcdef"
itera = Iterator1(str1)
print(itera.__next__())
print(itera.__next__())

6.2 使用类实现斐波拉契

class Fibonacci():
    def __init__(self):
        self.a = 0
        self.b = 1
    def __iter__(self):
        return self
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a

f = Fibonacci()
for i in range(9):
    print(f.__next__(), end=' ')

7、经典类/新式类

python2—经典类、新式类
只有显示地继承了object的类称为新式类,其他都是经典类
继承了python内置类的类称为新式类,其他都是经典类

python3—新式类
默认都是继承object,所有都是新式类

#类的定义
class A():
    pass
class B:
    pass
class C(object):
    pass
    
# 在python2种 A、B 两类都属于经典类,只有C是新式类
# 在python3中 默认会继承object类,所有A、B、C都是新式类

经典类和新式类的最大区别就是有没有继承object类,还有继承顺序的区别

type查看

>>> class A:pass  # Python2  
...
>>> a = A()
>>> type(a)  #通过type查看到的实例类型都叫做instance
<type 'instance'>  
>>> a.__class__ # 实例和类之间只能通过__class__ 属性进行关联 
<class __main__.A at 0x7f931dc95ec0>
>>> class A:pass  # Python3
...
>>> a = A()
>>> type(a)  #通过type查看到的实例类型就是类名
<class '__main__.A'>
>>> a.__class__
<class '__main__.A'>

继承顺序

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):  # MOR
    def test(self):
        print("from F")

f = F()
f.test()

Python 面向对象(一)_第2张图片

经典类:F——D——B——A——E——C(深度优先)
新式类:F——D——B——E——C——A(c3 算法,非广度有限)

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

举例:
A ——【A,Object】
B ——【B,A,Object】
D —— 【D,B,A,Object】
C —— 【C,A,Object】
E ——【E,C,A,Object】

F ——【F】+【D,B,A,Object】+【E,C,A,Object】
最后变成:【F,D,B】 因为A在【E,C,A,Object】在这个序列里面,所以A丢掉

8、属性和方法

属性:普通/静态

静态属性:类属性
普通属性:实例属性

方法:实例/静态/类方法

class A():
    name = '长沙'  # 静态属性

    def __init__(self):
        self.country = "china"  # 普通属性

    # 普通(实例)方法
    # 接收的第一个参数,就代表实例本身
    def normal_method(self, name):
        print("normal".center(20, '*'))
        print(self.name, name)

    # 使用classmethod修饰的方法,称为类方法
    # 接收的第一个参数,就代表类本身
    @classmethod
    def class_method(cls, name):
        print("class_method:".center(20, '*'))
        print(type(cls), cls)
        print(cls.name, name)  # 访问不了实例属性

    # 使用staticmethod修饰的方法,称为静态方法
    # 可以接收参数也可以不接,参数不代表实例和类本身
    @staticmethod
    def static_method(name):
        print("static_method:".center(20, '*'))
        print(A.name, name)  # 通过类名访问属性


# 通过实例调用实例、静态、类方法
a = A()
a.normal_method("实例调用实例方法")
a.class_method("实例调用类方法")
a.static_method("实例调用静态方法")

# 通过类调用实例、静态、类方法
# 通过类调用实例方法的时候,一定要传一个实例进去
A.normal_method(a, "类调用实例方法")
A.class_method("类调用类方法")
A.static_method("类调用静态方法")

结果:

*******normal*******
长沙 实例调用实例方法
***class_method:****
<class 'type'> <class '__main__.A'>
长沙 实例调用类方法
***static_method:***
长沙 实例调用静态方法
*******normal*******
长沙 类调用实例方法
***class_method:****
<class 'type'> <class '__main__.A'>
长沙 类调用类方法
***static_method:***
长沙 类调用静态方法

查看类属性和方法

• dir(Person):了解Person类里的变量和方法
• dir( p ) 或 p.__dir__() :查看对象的属性和方法
• Person.__dict__ :可以了解哪些是变量哪些是方法

内置属性及其功能

查看文档注释
第一个用三引号引起来的字符串
print(Parent.__doc__)
查看帮助文档
print(help(Parent))
查看对象和类空间
print(p.__dict__) #类的属性(包含一个字典,由类的数据属性组成)
print(Parent.__dict__) # 了解哪些是变量哪些是方法
查看对象属于那个类
print(p.__class__)
print(Parent.__name__) 查看类名
查看父类
print(Child.__bases__) 查看 继承的所有父类
print(Child.__base__)
print(Parent.__base__)
print(Parent.__bases__)
查看对象的哈希值
print(Parent.__hash__)
查看类定义所在的模块
print(a.__module__)
print(A.__module__)

9、Python中的下划线

不能被模糊导入
双下划线开头 只有类对象自己可以调用

class Parent:
    tmp = "tmp"
    _min = 1    # 保护成员
    __max = 10  # 私有属性

    def __init__(self):
        self.name = 'hejin'
        self._age = 18
        self.__desc = 'it' # 实例的私有属性,在类的内部调用

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

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

    def show(self):
        print(self.__max, self.__desc)  # 只能在类的内部调用
        print("这是一个普通方法")

class Child(Parent):
    def show(self):
        print(self.__max)

p = Parent()
c = Child()
# print(p.tmp, c.tmp)
# print(p._min, c._age, c._min)
# p._protectmake()
# c._protectmake()

# print(p.__max)
# p.show()
# c.show()

# 查看实例空间有哪些属性
print(p.__dict__)  # self.__desc——》 _Parent__desc _类名__属性/方法名
# print(p.__desc)
print(p._Parent__desc)  # 伪私有,双下划线开头的标识符改了名字

10、常用魔术方法和属性

在Python中,所有以双下划线__包起来的方法,统称为Magic Method(魔术方法),它是一种的特殊方法,普通方法需要调用,而魔术方法不需要调用就可以自动执行。

魔术方法在类或对象的某些事件出发后会自动执行,让类具有神奇的“魔力”。如果希望根据自己的程序定制自己特殊功能的类,那么就需要对这些方法进行重写。

Python中常用的运算符、for循环、以及类操作等都是运行在魔术方法之上的

构造函数

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

析构函数

__del__:在实例释放、销毁的时候自动执行的,通常用于做一些收尾工作, 如关闭一些数据库连接,关闭 打开的临时文件

class ATM:
    def __del__(self):  # 析构函数  再销毁之前需要做的事情
        print("执行del") # 这里做的收尾工作

    def __call__(self, name, age):
        print(f"my name is {name},my age is {age}")

a = ATM()
del a  # 关键字

调用方法

__call __ :把类实例化后的对象当做函数来调用的时候自动被调用

a("sc", 18) # 结果:my name is sc,my age is 18

其他魔术方法

  • __str__:给用户看的 ,返回对象的描述信息
    print(对象) 打印的结果就是调用repr
    没有定义__str__返回的就是__repr__
  • __repr__:更加官方的说明,给程序员的
    交互式环境里面直接实例名敲回车 返回的值就是调用的repr
    默认调用的是__repr__
    没有定义__repr__ 返回的是16进制地址

__getitem__:获取参数
__setitem__:设置数据
__delitem__:删除数据

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:")
        self.data[key] = value

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

a = A()
a["name"] = "hejin"  #set data:
print(a.data)        #{'name': 'hejin'}
print(a["name"])     #get data:  hejin
del a["name"]        #delete data:
print(a.data)        #{}

*• __eq__(self, other) 定义了等号的行为, ==
•__ne__(self, other) 定义了不等号的行为, !=
• __lt__(self, other) 定义了小于号的行为, <
• __gt__(self, other) 定义了大于等于号的行为, >=
•__add__(self, other) +运算
• __mul__(self, other) 运算
•__len__(self, other) 获得长度

练习:自定义异常

class LenError(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return self.msg

lst = [1, 2, 3, 4, 5]
try:
    if not 8 <= len(lst) <= 10:
        raise LenError("长度不在8-10之间")
except LenError as er:
    print(er)

11、Python自省

在计算机编程中,自省是指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。自省向程序员提供了极大的灵活性和控制力。

自省有4个方法 :

  • hasattr(obj, ‘name’): 检查是否含有成员
    判断一个对象obj里是否有对应的name_str字符串的方法
print(hasattr(math, "xx"))  # 判断math有没有xx属性
print(hasattr(math, "sin"))
print(math.sin(90))
  • setattr(obj, ‘age’, 18): 设置成员
  • getattr(obj, ‘name’): 获取成员
    根据字符串去获取obj对象里的对应的方法的内存地址
  • delattr(obj, ‘name’): 删除成员
print(setattr(math, "xx", 1))   # None 设置math的xx属性为1
print(getattr(math, "xx"))      # 1    获取math的xx属性
print(delattr(math, "xx"))      # None 删除math的xx属性
import math
class A:
    name = "sc"
    __max = "max"

    def f1(self):
        print("i am f1")

a = A()
print(hasattr(a, "name"))    #True
if hasattr(a, "f1"):
    print(getattr(a, "name")) #sc
if hasattr(a, "f1"):
    print(getattr(a, "f1")())  #i am f1 和None

def f2():
    print("i am f2")  

setattr(a, "f3", f2)
a.f3()         #i am f2

你可能感兴趣的:(python笔记,python,开发语言,后端)