Python基础(十一)-面向对象

三种编程范式:

1、函数式编程:函数指数学意义上的函数

由于命令式编程语言也可以通过类似函数指针的方式来实现高阶函数,函数式的最主要的好处主要是不可变性带来的。没有可变的状态,函数就是引用透明(Referential transparency)的和没有副作用(No Side Effect)。

一个好处是,函数即不依赖外部的状态也不修改外部的状态,函数调用的结果不依赖调用的时间和位置,这样写的代码容易进行推理,不容易出错。这使得单元测试和调试都更容易。

不变性带来的另一个好处是:由于(多个线程之间)不共享状态,不会造成资源争用(Race condition),也就不需要用锁来保护可变状态,也就不会出现死锁,这样可以更好地并发起来,尤其是在对称多处理器(SMP)架构下能够更好地利用多个处理器(核)提供的并行处理能力
View Code

2、面向过程编程:

3、面向对象编程:

面向对象编程--类与对象

类与对象初识

类:具有相同属性的事物的集合,只是一个抽象概念(Python中将具有相同数据属性和函数属性(方法)的对象集合的抽象定义为一个类)

对象:就是一个具体存在的事物(Python中由类实例化得到的就叫做一个对象(实例))

实例化:由类生成一个对象(实例)的过程就叫实例化

class China:#类名
    "this is China"  #类的文档字符串

capital=Beijing #类的数据属性 def __init__(self,a,b): #类的初始化函数(定义对象的数据属性) self.a=a self.b=b def func(self): #类的函数属性(默认返回None,也可以加return) pass obj=China(a,b) #实例化 #从结构上类似函数的定义

实例化的过程实质就是执行类的初始化函数,得到一个数据属性字典

查看所有属性:dir(obj) 返回列表,只有属性名
查看类的属性字典:print(China.__dirt__)

obj.func() # 对象调用类方法 Python默认会将obj传到第一个位置参数,而类调用方法则没有
China.func(obj) # 类调用方法,如果有参数就必须传参
# 对象可以调用类的所有属性,但类则无法调用对象里的属性(类似函数的作用域)

## 类和对象调用方法都是类名(对象名).函数名,没有点的调用,直接写个变量名,那就是类外的变量

分类:

Python2中有经典类与新式类之分,而Python3中则统一按照新式类处理(之后类的优先顺序会具体看到)

class a:       #经典类
    pass

class a(父类):#新式类 二者区别就在有无继承父类
    pass 

以上是Python2中类,Python3中都是新式类,默认继承object类,继承顺序是根据c3算法,可以通过__mro__方法查看

类与对象的属性操作(增、删、改、查)

#类属性又称为静态变量,或者是静态数据。这些数据是与它们所属的类对象绑定的,不依赖于任何类实例。
#如果你是一位Java或C++程序员,这种类型的数据相当于在一个变量声明前加上static关键字。
class Chinese:
    country='China'
    def __init__(self,name):
        self.name=name

    def play_ball(self,ball):
        print('%s 正在打 %s' %(self.name))

def say_word(self,word):
    print('%s 说 %s' %(self.name,word))

#查看类属性
print(Chinese.country)

#修改类属性
Chinese.country='CHINA'
print(Chinese.country)

#删除类属性
del Chinese.country

#增加类属性
Chinese.Country='China'
Chinese.location='Asia'
print(Chinese.__dict__)

#类的数据属性增删改查与函数属性是一样的
Chinese.say_word=say_word
print(Chinese.__dict__)
alex_obj=Chinese('alex')
Chinese.say_word(alex_obj,'我是一只来自北方的狼狗')
类属性操作
class Chinese:
    country='China'
    def __init__(self,name):
        self.name=name


    def play_ball(self,ball):
        print('%s 正在打 %s' %(self.name,ball))


p1=Chinese('alex')
print(p1.__dict__)

#查看对象属性  (如果对象没有则会调用类,类也没有就报错)
print(p1.country) 

#修改、增加
p1.country='A'  这是赋值,如果有这个属性就是修改,没有就是增加
print(p1.__dict__)   #此时就是增加
p1.country='B'        #此时就是修改

#删除
del p1.country  #没有该属性会报错
对象属性操作

对象与类的动态添加方法

class Student(object):
    pass

def set_age(self,age):    #定义一个函数作为实例方法
    self.age=age

from types import MethodType
s=Student()
s.set_age=MethodType(set_age,s)  #给实例绑定一个方法==> 类具有该方法吗?或者说其他实例能调用该方法吗?
s.set_age(25)
print(s.age)  # 直接绑在实例s上

s1=Student()
s1.set_age(20)
print('实例s1--',s1.age)  #显然其他实例没有该方法



###############
25
  File "E:/PyCharm/Personal_practice/suanfa/paixu.py", line 54, in <module>
    s1.set_age(20)
AttributeError: 'Student' object has no attribute 'set_age'
给实例

注意:

1.类实例化得到对象,实质就是执行了类的__init__函数,得到一个数据属性字典,所有说对象没有函数属性,但可以调用类的函数属性

2.类和对象调用属性使用 类名(对象).属性名,直接写个属性名就是调用类外的变量,切记 !

age=10000000000
class Chinese:
    country='China'
    age=10
    l=[]

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

    def play_ball(self,ball):
        print('%s 正在打 %s' %(self.name,ball))

p1=Chinese('wupeiqi')  #实例化
p2=Chinese('alex')

p1.age=100  #给对象的age数据属性赋值,没有该属性的话就是增加
p2.age=101

print(p1.age,p2.age)
print(age)   #这就是类外的变量调用
print(Chinese.age)

p1.l.append('p1')  #对p1.l列表进行追加,没有就是对类列表追加
p2.l.append('p2')   #同上,操作对象是列表
print(Chinese.l)
print(p1.l)
print(p2.l)
View Code

3.对象调用函数属性,Python会自动在第一个位置参数加上对象本身,类则没有,切记 !

静态属性、类方法、静态方法

初衷都是为了实现类直接能调用方法(本来必须传个对象)

静态属性没什么卵用,返回个property object

类方法可以接收返回值,方法内无法调用对象的数据属性self.a,可以调用类的数据属性cls.b

静态方法也可以接收返回值,方法内既不可以调用类的数据属性也不可以调用对象数据属性,而且它不像以上两种方法必须有个位置参数(self,cls)

三者比较结果:类方法相对灵活些

class Room:
    x=1
    y='hei'
    def __init__(self,name,owner,length,wide,high):
        self.name=name
        self.owner=owner
        self.length=length
        self.wide=wide
        self.high=high
    @property             #静态属性 给类调用方法,对象也可以调用该方法
    def cal_area(self):
        # print('%s 住的 %s 面积是 %s'%(self.owner,self.name,self.length*self.wide))
        return self.length*self.wide

r1 = Room('cesuo', 'alex', 10, 10, 10)
print(r1)
print(Room.cal_area)  #感觉没什么卵用 得到一个proprety object so能干嘛?
r1.cal_area=10
print(r1.cal_area)  #对象照样用
print(r1.__dict__)


    def cal_volume(self):
        print('%s住的%s体积是%s'%(self.owner,self.name,self.length*self.wide*self.high))

    @classmethod        #类方法:专门给类直接调类的函数属性使用(这是存在的初衷),(因为之前调用必须传入实例)与实例无关,
    def fuck(cls,a):
        cls.a=a
        return a
        return cls.y         #可以调用类的数据属性
        return self.length*self.wide  #类方法内无法调用初始化函数中对象数据属性

r1 = Room('cesuo', 'alex', 10, 10, 10)
print(r1.fuck())
l=Room.fuck(1)     #类调用之后能得到返回值  这个就有些用了
print(l)

    @staticmethod
    def yoyo(a,b,c):      #可以没有位置参数,不像以上两种方法,必须传个位置参数self,cls
        # print(a,b,c)
        # return self.name  #方法内无法调用对象数据属性
        # return x          #无法调用类的数据属性
        return a
print(Room.yoyo(1,2,3))
一个例子全部搞定

继承

继承有两种用途:

一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)

二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能

组合

类跟类之间没有共同点,但之间有关联或包含关系,比如老师属于某个学校,学生属于某个学校,课程属于某个老师

比如手、脚、头、躯干,都属于身体(或者说组成身体)

用途:

1、做关联

2、小的类组成大的类

class Hands:
    pass
class Feet:
    pass
class Head:
    pass
class Trunk:
    pass
class Body:
    'this is body'
    def __init__(self,name,hand,feet,head,trunk):
        self.name=name
        self.hand=Hands
        self.foot=feet
        self.head=Head
        self.trunk=Trunk
事例1
class School:
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr
    def tell_info(self):
        print('School(%s,%s)' %(self.name,self.addr))


class Course:
    def __init__(self,name,price,period,school):
        self.name=name
        self.price=price
        self.period=period
        self.school=School  #此处就是组合的体现(与上面的School相关联)

#其实关联的实际意义就体现在数据的重复利用,比如已经创建了两个School实例,再建Course实例的时候就不需要再手动传输school,只需在已经创建的两个School实例中选择即刻
事例2

接口与归一化设计

class Interface:#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。
    def read(self): #定接口函数read
        pass

    def write(self): #定义接口函数write
        pass


class Txt(Interface): #文本,具体实现read和write
    def read(self):
        print('文本数据的读取方法')

    def write(self):
        print('文本数据的读取方法')

class Sata(Interface): #磁盘,具体实现read和write
    def read(self):
        print('硬盘数据的读取方法')

    def write(self):
        print('硬盘数据的读取方法')

class Process(Interface):
    def read(self):
        print('进程数据的读取方法')

    def write(self):
        print('进程数据的读取方法')
接口

实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。

继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化

抽象类

跟接口类类似,不同的地方是抽象类只能继承,不能实例化

#_*_coding:utf-8_*_

#一切皆文件
import abc        #利用abc模块实现抽象类

class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod        #定义抽象方法,无需实现功能
    def read(self):
        '子类必须定义读功能'
        pass

    @abc.abstractmethod         #定义抽象方法,无需实现功能
    def write(self):
        '子类必须定义写功能'
        pass

# class Txt(All_file):
#     pass
#
# t1=Txt()       #报错,子类没有定义抽象方法

class Txt(All_file):     #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('文本数据的读取方法')

    def write(self):
        print('文本数据的读取方法')

class Sata(All_file):     #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('硬盘数据的读取方法')

    def write(self):
        print('硬盘数据的读取方法')

class Process(All_file):     #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('进程数据的读取方法')

    def write(self):
        print('进程数据的读取方法')

wenbenwenjian=Txt()

yingpanwenjian=Sata()

jinchengwenjian=Process()

#这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)
Python中实现抽象类

子类调用父类方法super

class A:
    def a(self):
        print('A.a')

    def d(self):
        print('A.d')

class B:
    def b(self):
        print('B.b')

class C(A,B):
    def a(self):
        super().a()
        print('C.a')

    def c(self):
        print('C.c')

    def d(self):
        print('C.d')

class D(C):
    def c(self):
        print('D.c')


print(D().a())
super

注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表

当然也可以使用 父类名.父类方法() 的方式直接写在子类代码中,耦合性太强

原文地址:https://www.cnblogs.com/chenzhuo-/p/6202094.html