python ---------------------面向对象

面向对象

  • 语言的分类
    面向机器
    抽象成计价器指令,机器容易理解
    代表:汇编语言

面向过程
做一件事情,排出个步骤,第一步干什么,第二步干什么,如果出现情况A,做什么处理,如果出现了情况B,做什么处理
问题规模小,可以步骤化,按部就班处理
代表:C语言

面向对象OOP
随着计算机需要解决的问题规模扩大,情况越来越复杂.需要很多人,很多部门协作,面向过程编程不太适合了.
代表:C++,java,Python

  • 面向对象
    什么是面向对象
    一种认识世界,分析世界的方法论,将完事万物抽象为各种对象
    类class
    类是抽象的概念,是万事万物的抽象,是一类事物的共同特征的集合
    对象instance,object
    对象是类的具象,是一个实体
    对于我们每个人这个个体,都是抽象概念人类的不同实体

举例:
你吃鱼
你,就是对象;鱼,也是对象;吃就是动作
你是具体的人,是具体的对象.你属于人类,人类是个抽象的概念,是无数具体的人的个体的抽象.
鱼也是具体的对象,就是你吃的这一条具体的鱼,这条鱼属于鱼类,鱼类是无数的鱼抽象出来的概念.
吃时动作,也是操作,也是方法,这个吃是你的动作,也就是人类具有的方法.如果反过来,鱼吃人,吃就是鱼类的动作了.
吃,这个动作,很多动物都具有的动作,人类和鱼类都属于动物类,而动物类是抽象的概念,是动物都有吃的动作,但是吃法不同而已.
你驾驶车,这个车也是车类的具体的对象(实例),驾驶这个动作是鱼类不具有的,是人类具有的方法
属性:它是对象状态的抽象,用数据结构来描述
操作:它是对象行为的抽象,用操作名和实现该操作的方法 来描述

  • 哲学
    一切皆对象
    对象是数据和操作的封装
    对象是独立的,但是对象之间可以互相 作用
    目前OOP是最接近人类认知的编程范式

面向对象3要素

1,封装
组装:将数据和操作组装到一起
隐藏数据:对外只暴露一些接口,通过接口访问对象,比如驾驶员使用汽车,不需要了解汽车的构造细节,只需要直到使用什么不见怎么驾驶就行,踩了油门就跑,可以不了解其中的激动原理
2,继承
多复用:继承来的就不用自己写了
多继承少修改,OCP(open-closed Principle),使用继承来改变,来体现个性
3,多态
面向对象编程最灵活的地方,动态绑定

Python的类

定义

class ClassName:
    语句块

1,必须使用class关键字
2,类名必须是大驼峰命名
3,类定义完成后,就产生了一个类对象,绑定到了标识符ClassName上

class MyClass:
    """A example class"""
    x = 'abc'
    
    def foo(self):
        return 'My Class'

类对象及类属性

  • 类对象,类的定义执行后会生成一个类对象
  • 类的属性,类定义中的变量和类中的方法都是类的属性
  • 类变量,上例中x是类MyClass的变量
    MyClass中,x,foo都是类的属性,__doc__也是类的特殊属性
    foo方法是类的属性,如同吃是人类的方法,凡是每一个具体的人才 能吃东西,也就是说吃是人的实例能调用的方法
    foo是方法method,本质上就是普通的函数对象function,它一般要求至少有一个参数,第一个形式参数可以是self(self只是个管用的标识符,可以换名字),这个参数位置几句留给了self.
    self自带当前实例本身
实例化
a = MyClass()#实例化

使用上面的语法,在类对象名称后面加上一个括号,就调用实例化方法,完成实例化.
实例化就真正创建一个该类的对象(实例),例如

tom = Person()
jerry = Person()

上面的tom,jerry都是Person类的实例,通过实例化成2个实例.
每次实例化后获得的实例,是不同的实例,即使是使用同样的参数实例化,也得到不一样的对象.
Python类实例化后,会自动调节__init__方法,可以不定义,如果没有定义会在实例化后隐式调用.
作用:对实例进行初始化

class MyClass:
    def __init__(self):
        print('init)

print(MyClass)
print(MyClass())
a = MyClass()

初始化函数可以多个参数,请注意第一个位置参数,必须是self,例如__init__(self,name,age)

class MyClass:
    """A example class"""
    def __init__(self,name,age):
        self.name = name
        self.age = age
    
    def showage(self):
        print("{} is {}".format(self.name,self.age))
        
tom = Person('tom',30)
jerry = Person('jerry',25)
print(tom.name,jerry.age)
jerry.age += 1
print(jerry.age)
jerry.showage()

注意:inint()方法不能有返回值,也就是只能是return None
实例对象instance
类实例化后一定会获得一个类的实例.
上例中的tom,jerray就是Person类的实例
__init__方法的第一个参数self就是指代某一个实例自身.
类实例化后,得到一个实例对象,调用方法时 采用jerray.showage()的方式,实例对象会绑定到方法上.
但是该函数签名是showage()调用时,会把方法的调用者jerrar实例作为第一参数self的实参传入.
self.name就是jerrar对象的name,name是保存在了jerray对象上,而不是Person类上,所以,称为实例变量
self

class MyClass:
    def __init__(self):
        print(1,'self in init = {}'.format(id(self)))

    def showself(self):
        print(2,'self in showself() = {}'.format(id(self)))

c = MyClass()
print(3, 'c = {}'.format(id(c)))
print('-' * 30)
c.showself()

#打印结果为
1 self in init = 257636858512
3 c = 257636858512
----------------------------
2 self in showself() = 257636858512

上例说明,self就是调用者,就是c对应的实例对象
slef这个名字只是一个管理,它可以修改,但是请不要修改,否则影响代码的可读性

实例变量和类变量
class Person:
    age = 3
    def __init__(self,name):
        self.name = name

tom = Person('tom')
jerry = Person('jerry')

print(tom.name,tom.age)
print(jerry.name,jerry.age)
print(Person.age)
Person.age = 30
print(Person.age,tom.age,jerry.age)

#运行结果
tom 3
jerry 3
3
30 30 30

实例变量是每一个实力自己的变量,是自己独有的,类变量是类的变量,是类的所有实例共享的属性和方法

特殊属性含义
__name__对象名
--class__对象的类型
__dict__对象的属性的字典
--qualname__类的限定名

注意:
python中每一种对象都拥有不同的属性.函数,类都是对象,类的实例也是对象.
举例

class Person:
    age = 3
    def __init__(self,name):
        self.name = name

print('----class----')
print(Person.__class__,type(Person))
print(sorted(Person.__dict__.items()),end = '\n\n')

tom = Person('tom')
print('----instance tom----')
print(tom.__class__,type(tom))
print(sorted(tom.__dict__.items()),end = '\n\n')

print("----tom's class----")
print(tom.__class__.__name__)
print(sorted(tom.__class__.__dict__.items()),end = '\n\n')

#返回结果

<class 'type'> <class 'type'>
[('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x000001B9654441E0>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]

----instance tom----
<class '__main__.Person'> <class '__main__.Person'>
[('name', 'tom')]

----tom's class----
Person
[('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x000001B9654441E0>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]

上例中,可以看到类属性保存在类的__dict__中,实例属性保存在实例的__dict__中,如果从实例访问类的属性,也可以借助__class__找到所属的类,在通过类来访问类属性,例如 tom.__class__.age.

再看下下面的代码

class Person:
    age = 3
    height = 170

    def __init__(self,name,age=18):
        self.name = name
        self.age = age

tom = Person('tom')
jerry = Person('jerray',20)

Person.age = 30
print(1,Person.age,tom.age,jerry.age)
print(2,Person.height, tom.height, jerry.height)
jerry.height = 175
print(3,Person.height, tom.height, jerry.height)

tom.height += 10
print(4,Person.height, tom.height, jerry.height)

Person.height += 15
print(5,Person.height, tom.height, jerry.height)

Person.weight = 70
print(6,Person.weight, tom.weight, jerry.weight)


print(7,tom.__dict__.items())

总结
是类的,也是这个类所有实例的,起始例都哦可以访问到;
是实例的,就是这个实例自己的,通过类访问不到.
类变量是属于类的变量,这个类的所有实例可以共享这个变量

对象(实例或类)可以动态的给自己增加一个属性(复制即定义一个新的属性)
实例.__dict__[变量名]访问变量,将不会按照上面的查找顺序找变量了,这是指明使用字典的key查找,不是属性查找.
一般来说,类变量可使用全大写来命名
装饰一个类
需求:为一个类通过装饰,增加一些雷属性,加入能否给一个类增加一个NAME类属性并提供属性值

# 增加类变量
def add_name(name):
    def wapper(cla):
        cla.NAME = name
        return cla
    return wapper

@add_name('tom')
class Person:
    AGE = 3
    
print(Person.NAME)

之所以能够装饰,本质上是为类对象动态的添加了一个属性,而Person这个标识符指向这个类对象.
类方法和静态方法
前面的李志中定义的__init__等方法,这些方法本身都是类的属性,第一个参数必须是self,而self必须只想一个对下个,也就是类实例化之后,由实力来调用这个方法.

  • 普通函数
class Person:
    def normal_method():
        print('normal')

Person.normal_method()
Person().normal_method()

print(Person.__dict__)

Person.normal_method()
可以放在类中定义,因为这个方法只是被Person这个名词空间管理的一个普通的方法, normal_method是Person的一个属性而已
由于normal_method在定义的时候没有指定self,所以不能完成实例对象绑定,不能用
Person().normal_method()调用.
注意:虽然语法是对的,但是没有人这么用,也就是说禁止这么写

  • 类方法
class Person:
    @classmethod
    def class_method(cls):
        print('class = {0.__name__} ({0})'.format(cls))
        cls.HEIGHT = 170

Person.class_method()
print(Person.__dict__)

1,在类定义中,使用@classmethod装饰器修饰的方法
2,必须至少有一个参数,且第一个参数留给了cls,cls指代调用者,即类对象自身
3,cls这个表示符可以是任意合法名称,但是为了易读,请不要修改
4,通过cls可以直接操作类的属性

  • 静态方法
class Person:
    @classmethod
    def class_method(cls):
        print('class = {0.__name__} ({0})'.format(cls))
        cls.HEIGHT = 170

    @staticmethod
    def static_methd():
        print(Person.HEIGHT)

Person.class_method()
Person.static_methd()
print(Person.__dict__)

1,在类定义中,使用@staticmethod装饰器修饰的方法
2,调用时,不会隐式的传入参数
静态方法,只是表明这个方法属于这个名词空间.函数跪在一起,方便组织管理
方法的调用
类可以定义这么多种方法,究竟如何调用他们

class Person:
    def method(self):
        print("{}is method ".format(self))

    @classmethod
    def class_method(cls):
        print('class = {0.__name__} ({0})'.format(cls))
        cls.HEIGHT = 170

    @staticmethod
    def static_methd():
        print(Person.HEIGHT)

print('~~~~~~~~~类访问')
print(1,Person.method())  #报错,self参数没有,类没有生成实例
print(2,Person.class_method())  #正常执行,因为有class_method装饰器把cls参数传入
print(3,Person.static_methd())  #正常执行,static_methd方法因为有staticmethod装饰器不需要传参
print(Person.__dict__)
print('~~~~~实例访问')
print('tom---------')
tom = Person()
print(4,tom.method())
print(5,tom.class_method())
print(6,tom.static_methd())
print('jerry--------')
jerry = Person()
print(7,jerry.method())
print(8,jerry.class_method())
print(9,jerry.static_methd())

person.method()

类几乎可以调用所有内部定义的方法, 但是调用普通方法时会报错,原因时第一参数必须是类的实例.

Person().method()
实例也几乎可以调用所有的方法,普通的函数的调用一般不可能出现,因为不允许这么定义
总结:
类除了普通方法都可以调用,普通方法需要对象的实例作为第一参数.
实例可以调用所有类中的方法(包括类方法,静态方法),普通方法传入实例自身,静态方法和类方法需要找到实例的类
补充:

class Person:
    def method(self):
        print("{}is method ".format(self))

tom = Person()
tom.method()  #实例的方法可用
Person.method()   #类调用方法,没有self参数
Person.method(tom)   #类调用方法,传入了tom
tom.__class__.method(tom)#tom的class是Person,类调用方法,传入tom

tom.method()调用的时候,会绑定实例,调用method方法时,实例tom会注入到method中,这样第一参数就满足了
Person.method(),使用类调用,不会有实例绑定,调用method方法时,就缺少第一参数,可以手动填入.

访问控制

私有(Private)属性

class Person:
    def __init__(self,name,age=18):
        self.name = name
        self.age = age
    def growup(self,i = 1):
        if i > 0 and i < 150:
            self.age += i

p1 = Person('tom')
p1.growup(20)
p1.age = 160
print(p1.age)

上例,本来是想通过方法控制属性,但是由于属性在外部访问,或者说可见,就可以直接绕过方法,直接修改这个属性.
Pythob提供了私有属性可以解决这和问题.
私有属性
使用双下划线开头的属性名,就是私有属性

class Person:
    def __init__(self,name,age=18):
        self.name = name
        self.__age = age
    def growup(self,i = 1):
        if i > 0 and i < 150:
            self.__age += i

p1 = Person('tom')
p1.growup(20)
print(p1.__age)

通过实验可以看出,外部已将访问不到__age了,age根本就没有定义,更是访问不到.
试一下创建一个方法返回私有值

class Person:
    def __init__(self,name,age=18):
        self.name = name
        self.__age = age
        
    def growup(self,i = 1):
        if i > 0 and i < 150:
            self.__age += i
    def getage(self):
        return self.__age

print(Person('tom').getage())

私有变量的本质
外部访问不到,能够动态增加一个__age吗?

class Person:
    def __init__(self,name,age=18):
        self.name = name
        self.__age = age

    def growup(self,i = 1):
        if i > 0 and i < 150:
            self.__age += i
            
    def getage(self):
        return self.__age

p1 = Person('tom')
p1.growup(20)
p1.__age = 28
print(p1.__age)
print(p1.getage())
print(p1.__dict__)

我们来看下__dict__中的数据,{‘name’: ‘tom’, ‘_Person__age’: 38, ‘__age’: 28}
私有变量的本质:
类定义的时候,如果声明一个实例变量的时候,使用双下划线,Python解释器会将其改名,转换名称为_类名_变量名 的名称,所以用原来的名称访问不了到

class Person:
    def __init__(self,name,age=18):
        self.name = name
        self.__age = age

    def growup(self,i = 1):
        if i > 0 and i < 150:
            self.__age += i

    def getage(self):
        return self.__age

p1 = Person('tom')
p1.growup(20)
p1.__age = 28
print(p1.__age)
print(p1.getage())
print(p1.__dict__)

p1._Person_age = 15
print(p1.getage())
print(p1.__dict__)

从上例可以看出,知道了私有变量的新名称,既可以直接从外部访问 到,并可以修改了

保护变量

在变量前使用下划线,称为保护变量

class Person:
    def __init__(self,name,age=18):
        self.name = name
        self._age = age

    def _getname(self):
        return self.name

    def __getage(self):
        return self._age

tom = Person('tom')
print(tom._getname())
print(tom.__getage())   #报错无次属性
print(tom.__dict__)
print(tom.__class__.__dict__)
print(tom._Person__getage())

私有方法的本质
但下划线的方法只是开发者之间的约定,解释器不做任何改变.
双下划线的方法,是私有办法,解释器会改名,改名策略和私有变量相同,_类名+方法名.
方法变量的私有转换都可以在类的__dict__中找到
私有成员的总结
在Python中使用_单下划线或者__双下划线来表示一个成员被保护或者被私有化隐藏起来.
但是,不管使用什么杨的访问控制,都不能真正阻止用户修改类的成员,Python中没有绝对安全的保护成员或者私有成员.
因此,前导的下划线只是哟中警告或者提醒,请遵守这个约定,除非真有必要,不要修改或者使用保护成员或者私有成员,更不要修改它们.

补丁

可以通过修改或者替换类的成员,使用调用的方式没有改变,但是,类童工的功能可能已经改变了.
猴子补丁(Monkey Patch):
在运行时,堆属性,方法,函数等进行动态替换.
其目的往往是为了通过替换,修改来增强,扩展原有代码的能力.
黑魔法,慎用.

# test1
from test2 import Person
from test3 import get_score

def monkeypatch4Person():
    Person.get_score = get_score

monkeypatch4Person()  #打补丁


if __name__ == "__main__":
    print(Person().get_score())
    
# test2
class Person:
    def get_score(self):
        # connect to mysql
        ret = {'English':78,'Chinse':86,'History':82}
        return ret

# test3
def get_score(self):
    return dict(name=self.__class__.__name__,English=88,Chinese=90,History=85)

上例中,假设Person类get_score方法是从数据库拿数据,但是测试的售后,不方便.
为了测试时方便,使用猴子补丁,替换了get_score方法,返回模拟的数据
属性装饰器
一般好的设计是:把实例的某些属性保护起来,不让外部直接访问,外部使用getter读取属性和setter方法设置属性

class Person:
    def __init__(self,name,age=18):
        self.name = name
        self.__age = age

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self,age):
        self.__age = age
        
    @age.deleter
    def age(self):
        # def self.__age
        print('del')
        
tom = Person('tom')
print(tom.age)
tom.age = 20
print(tom.age)
del tom.age

特别注意:使用property装饰器的时候这三个方法同名

property装饰器
后面跟的函数名就是以后的属性名,它就是getter,这个必须有,有了它至少是只读属性

setter装饰器
与属性名同名,且接受两个参数,第一个是self,第二个是将要赋值的值.有了它,属性可写

deleter装饰器
可以控制是否删除属性,很少用

property装饰器必须在前,setter,deleter装饰器在后
property装饰器能通过简单的方式,把对方法的操作变成堆属性的访问,并起到了一定隐藏效果

class Person:
    def __init__(self,name,age=18):
        self.name = name
        self.__age = age
        
    def getage(self):
        return self.__age
    
    def setage(self,age):
        self.__age = age
        
    def delage(self):
        # del self.__age
        print('del')
        
    age = property(getage, setage, delage, 'age property')

tom = Person('tom')
print(tom.age)
tom.age = 20
print(tom.age)

还可以如下

class Person:
    def __init__(self,name,age=18):
        self.name = name
        self.__age = age
        
    age = property(lambda self:self.__age)
    
tom = Person('tom')
print(tom.age)

对象的销毁
类中可以定义__del__方法,称为析构函数(方法).
作用:销毁类的实例的时候调用,以释放占用的资源.其中就放些清理资源的代码,比如释放连接
注意这个方法不能引用对象的真正的销毁,只是对象销毁的时候会自动调用它,

使用del语句删除实例.引用计数减1,当引用计数为0时,会自动调用__del__方法.
由于Pyhton实现了垃圾回收机制,不能确定对象合适执行垃圾回收.

import time

class Person:
    def __init__(self,name,age = 18):
        self.name = name
        self.__age = age

    def __del__(self):
        print('delete {}'.format(self.name))

def test():
    tom = Person('tom')
    tom.__del__()
    tom.__del__()
    tom.__del__()
    tom.__del__()
    print("===============start==============")
    tom2 = tom
    tom3 = tom2
    del tom
    del tom2
    print('~~~~~~~~~~~~~~~~~~~')
    del tom3
    print('=================end')

test()

由于垃圾回收对象销毁时,才会真正清理都想,还会在回收对象之前自动调用__del__方法,除非你明确知道自己的目的,建议不要手动调用这个方法
方法重载
其他面向对象的高级语言中,会有重载的概念
所谓重载,就是同一个方法名,但是参数的个数,类型不一 样,就是同一个方法的重载.

Python不需要重载
Python中,方法(函数)定义中,形参非常灵活,不需要指定类型(就算制定了也只是一个说明而非约束),参数个数也不固定(可变参数).一个函数的定义可以实现很多种不同形式实参的调用.所以Python不需要方法的重载.

或者说Python语法本身就实现了其他语言的重载
封装
面向对象三要素之一,封装Encapsulation
封装
将数据和操作组织到类中,即属性和方法
将数据隐藏起来,给使用者提供操作(方法).使用者通过操作就可以获取或者修改数据.getter和setter.
通过访问控制,暴露适当的数据给用户没改隐藏的隐藏起来,例如保护成员或私有成员

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值