Python3之面向对象

一、定义:面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)

  • 面向过程:根据业务逻辑从上到下写垒代码
  • 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
  • 面向对象:对函数进行分类和封装,让开发“更快更好更强...”

  1.创建类和对象:面向对象编程是一种编程方式,需要使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。

  

class 类名:
    静态属性1 = '静态属性1'              # 公开静态属性 只有是这个类的就有这个属性
    __静态私有属性2 = '__静态属性2'       # 私有静态属性 只有是这个类的就有这个属性
    def __init__(self):       # 初始化方法 self是对象,是一个必须传的参数
        self.name = 'felix'   #公开对象属性
        self.__age = 18      #私有对象属性

    def func1(self):        #类的动态公开方法 一般情况下必须传self参数
        pass

    def __func2(self):     #类的动态私有方法 一般情况下必须传self参数
        pass

对象 = 类名()   #类的实例化  创建对象
对象.静态属性1   #引用类的静态属性1
对象.func1()

  说明:

  • 关键字:class  使用关键字class 来创建一个类
  • 关键字:self   一般情况,必须传的一个参数,代表类本身
  • 创建对象:类名称后加括号即可
  • 类的公开属性和方法可以在外部调用
  • 类的私有属性和方法不能在类的外部调用,只能在类的内部调用
    import math
    class Circle:
        def __init__(self,r):
            self.r = r
        def area(self):
            return math.pi*(self.r**2)
        def perimeter(self):
            return 2*math.pi*self.r
    A = Circle(5)
    print(A.area())
    print(A.perimeter())

  2.静态属性  就是直接在类中定义的变量

  (1)类中的静态变量,可以被对象调用
  (2)对于不可变数据类型来说,类变量最好用类操作
  (3)对于可变数据类型来说,对象名的修改时共享的,重新赋值是独立的

  3.动态属性 就是类中定义的方法(函数)

  4.命名空间:类和对象分别存在不同的命名空间中

  4.组合:一个对象的属性值是另外一个类的对象

import math
class Circle:
    def __init__(self,r):
        self.r = r
    def area(self):
        return math.pi*(self.r**2)
    def perimeter(self):
        return 2*math.pi*self.r

class Ring:
    def __init__(self,outside_r,inside_r):
        self.outside_r = Circle(outside_r)
        self.inside_r = Circle(inside_r)

    def area(self):
        return self.outside_r.area() - self.inside_r.area()
    def perimeter(self):
        return self.outside_r.perimeter() + self.inside_r.perimeter()
R = Ring(20,10)
print(R.area())
print(R.perimeter())

 

、三大特性之封装

  封装就是在变量的左边加上双下划线,有以下三种情况:

1.类中的静态私有属性

2.类中的私有方法

3.对象的私有属性

所有的私有属性和方法都不能在类的外部调用。

class Person:
    __key = 123  # 私有静态属性
    def __init__(self,name,passwd):
        self.name = name
        self.__passwd = passwd   # 私有属性

    def __get_pwd(self):         # 私有方法
        return self.__passwd   #只要在类的内部使用私有属性,就会自动的带上_类名

    def login(self):          # 正常的方法调用私有的方法
        self.__get_pwd()

felix = Person('felix','felix666')
print(felix._Person__passwd)   # _类名__属性名
print(felix.get_pwd())  #报错,类外部不能调用私有属性

执行结果:
felix666
AttributeError: 'Person' object has no attribute 'get_pwd'

  

四、三大特性之继承

  1.定义

继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。

  2.单继承和多继承

class ParentClass1: #定义父类(基类、超类)
    pass

class ParentClass2: #定义父类(基类、超类)
    pass

class SubClass1(ParentClass1): #Subclass是子类(派生类)   单继承 ,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass

  实例:

class Animal:  #父类  基类  超类
    def __init__(self):
        print('执行Animal.__init__')
        self.func()
    def eat(self):
        print('%s eating'%self.name)
    def drink(self):
        print('%s drinking'%self.name)
    def func(self):
        print('Animal.func')

class Dog(Animal):  #子类 派生类
    def guard(self): #派生方法
        print('guarding')
    def func(self):
        print('Dog.func')
dog = Dog()
  • 父类中没有的属性,在子类中出现 叫做派生属性
  • 父类中没有的方法,在子类中出现 叫做派生方法
  • 只要是子类的对象调用,子类中有名字的,一定用子类的;子类中没有才找父类的,如果父类也没有就报错
  • 如果父类、子类都有的属性和方法,就使用子类的;如果还想用父类的(单独调用父类的),   调用方法:父类名.方法名(需要自己传self参数)

      调用方法:父类名.方法名(self)   #需要传自己的self参数

             super().方法名           #不需要自己传self  python3

  3.继承顺序

   那么问题又来了,多继承呢?

  • 是否可以继承多个类
  • 如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?
class F:
    def func(self):
        print('F')
class D(F):
    def func(self):
        super().func()
        print('D')

class E(F):
    def func(self):
        super().func()
        print('E')

class B(D):
    def func(self):
        super().func()
        print('B')

class C(E):
    def func(self):
        super().func()
        print('C')

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

a = A()
a.func()
print(A.mro())
执行结果:
F
E
C
D
B
A
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>]

新式类和经典类
关于多继承寻找父类的顺序:新式类广度优先 经典类深度优先
新式类中 有一个类名.mro方法 查看广度优先的继承顺序
在Python3中,有一个super方法,根据广度优先的继承顺序查找上一个类

  

  • Python的类可以继承多个类,Java和C#中则只能继承一个类
  • Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先广度优先(py3)

  4.接口类和抽象类

  • 接口类和抽象类都是面向对象的规范,所有的接口类和抽象类都不能实例化
  • 接口类是支持多继承的,抽象类不支持多继承

 

  (1)接口类

 接口类的单继承:

class Wechat(Payment):
    def pay(self,money):
        print('已经用微信支付了%s元'%money)

class Alipay(Payment):
    def pay(self,money):
        print('已经用支付宝支付了%s元' % money)

class Applepay(Payment):
    def pay(self,money):
        print('已经用applepay支付了%s元' % money)

def pay(pay_obj,money):  # 统一支付入口
    pay_obj.pay(money)

# wechat = Wechat()
# ali = Alipay()
app = Applepay()
# wechat.pay(100)
# ali.pay(200)
p = Payment()

 

 接口类的多继承:

# Tiger 走路 游泳
# Swan 走路 游泳 飞
# Eagle 走路 飞
from abc import abstractmethod,ABCMeta
class Swim_Animal(metaclass=ABCMeta):
    @abstractmethod
    def swim(self):pass

class Walk_Animal(metaclass=ABCMeta):
    @abstractmethod
    def walk(self):pass

class Fly_Animal(metaclass=ABCMeta):
    @abstractmethod
    def fly(self):pass

class Tiger(Walk_Animal,Swim_Animal):
    def walk(self):
        pass
    def swim(self):
        pass
class Eagle(Fly_Animal,Walk_Animal):
    def fly(self):
        pass
    def walk(self):
        pass
class Swan(Swim_Animal,Walk_Animal,Fly_Animal):
    def walk(self):
        pass
    def swim(self):
        pass
    def fly(self):
        pass

t = Tiger()
e = Eagle()
s = Swan()

  

  (2)抽象类

import abc #利用abc模块实现抽象类

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

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

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)

  

小结:


 # java :

java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题

但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题

# python

python中没有接口类概念,但是直接用多继承,实现了接口类  abc模块中的meraclass=ABCMeta @abstructmethod

python中支持抽象类 : 一般情况下 单继承 不能实例化

Python 支持单继承也支持多继承 所以对于接口类和抽象类的区别就那么明显了


 

、三大特性之多态

  Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”。

  如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型 即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’

 

优点 : 松耦合 每个相似的类之间都没有影响
缺点 : 太随意了,只能靠自觉
在面向对象方法中一般是这样表述多态性:
向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。
也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同

 

 多态性:

peo=People()
dog=Dog()
pig=Pig()

#peo、dog、pig都是动物,只要是动物肯定有talk方法
#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()

#更进一步,我们可以定义一个统一的接口来使用
def func(obj):
    obj.talk()

 

#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
class TxtFile:
    def read(self):
        pass

    def write(self):
        pass

class DiskFile:
    def read(self):
        pass
    def write(self):
        pass

 

六、类的装饰器

  类的内置函数

  1.@property   作用是在新式类中返回属性值。

实例1:

import math
    class Circle:
        def __init__(self,r):
            self.r = r
        @property
        def perimeter(self):  #用了类的装饰器 property 后,不能在此处加参数
            return 2 * math.pi * self.r
        @property
        def area(self):   #用了类的装饰器 property 后,不能在此处加参数
            return math.pi * (self.r ** 2)
 
c1 = Circle(5)
print(c1.area)   #用了 类的property装饰器后 area 变成了 c1的属性了
print(c1.perimeter)   #用了 类的property装饰器后 perimeter 变成了 c1的属性了

实例2:

class Person:
    def __init__(self,name):
        self.__name = name
    @property
    def name(self):
        return self.__name + '牛牛牛'
    @name.setter
    def name(self,new_name):
        self.__name = new_name

felix = Person('felix')
print(felix.name)
felix.name = 'all'
print(felix.name)

 

  2.@classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。

实例1:

class A(object):
    bar = 1
    def func1(self):  
        print ('foo') 
    @classmethod
    def func2(cls):
        print ('func2')
        print (cls.bar)
        cls().func1()   # 调用 foo 方法
 
A.func2()               # 不需要实例化

输出结果为:

func2
1
foo

  

实例2:

class A(object):

    # 属性默认为类属性(可以给直接被类本身调用)
    num = "类属性"

    # 实例化方法(必须实例化类之后才能被调用)
    def func1(self): # self : 表示实例化类后的地址id
        print("func1")
        print(self)

    # 类方法(不需要实例化类就可以被类本身调用)
    @classmethod
    def func2(cls):  # cls : 表示没用被实例化的类本身
        print("func2")
        print(cls)
        print(cls.num)
        cls().func1()

    # 不传递传递默认self参数的方法(该方法也是可以直接被类调用的,但是这样做不标准)
    def func3():
        print("func3")
        print(A.num) # 属性是可以直接用类本身调用的
    
# A.func1() 这样调用是会报错:因为func1()调用时需要默认传递实例化类后的地址id参数,如果不实例化类是无法调用的
A.func2()
A.func3()

  

实例3:

class Goods:
    __discount = 0.8
    def __init__(self,name,price):
        self.name = name
        self.__price = price
    @property
    def price(self):
        return self.__price * Goods.__discount

    @classmethod  #把一个方法 变成一个类中的方法 这个方法就直接可以被类调用,不需要任何依赖
    def change_discount(cls,new_diccount):  #修改折扣
         cls.__discount = new_diccount
apple = Goods('苹果',5)
print(apple.price)
apple.change_discount(0.5)
print(apple.price)
执行结果:
4.0
2.5

  

  3.@staticmethod  返回函数的静态方法。

该方法不强制要求传递参数,如下声明一个静态方法:

class C(object):
    @staticmethod
    def f(arg1, arg2, ...):
        ...

以上实例声明了静态方法 f,类可以不用实例化就可以调用该方法 C.f(),当然也可以实例化后调用 C().f()。

class C(object):
    @staticmethod
    def f():
        print('runoob');
 
C.f();          # 静态方法无需实例化
cobj = C()
cobj.f()        # 也可以实例化后调用

  

class Login:
    def __init__(self,name,password):
        self.name = name
        self.pwd = password
    def login(self):pass

    @staticmethod
    def get_usr_pwd():   # 静态方法  此处没有参数
        usr = input('用户名 :')
        pwd = input('密码 :')
        Login(usr,pwd)

Login.get_usr_pwd()

在完全面向对象的程序中,如果一个函数即和对象没有关系 也和类也没有关系,那么就用staticmethod将这个函数变成一个静态方法

 

七、反射

   python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射),是用字符串类型的名字去操作变量。

  反射使用的内置函数: hasattr、 getattr、 setattr、 delattr

  hasattr和getattr成对出现

  

class A:
    def fun(self):
    print('in func')

a = A()
a.name = 'felix'
a.age = 30

1.反射对象的属性 getattr
    ret = getattr(a,'name') #通过变量的字符串形式取到的值
    print(ret)
    print(a.__dict__)
    变量名 = input('>>>')  #func
    print(getattr(a,变量名))
    print(a.__dict__[变量名])

2.反射对象的方法 getattr
    ret = getattr(a,'func')
    ret()

3.反射类的属性 getattr
class A: price = 20 @classmethod def fun(self): print('in func') print(getattr(A,'price')) 4.反射类的方法 getattr # A.fun() if hasattr(A,'fun'): getattr(A,'fun')()

  

# 模块
    内置模块也能用反射

        import time
        print(time.strftime('%Y-%m-%d %H:%M:%S'))
        print(getattr(time,'strftime')('%Y-%m-%d %H:%M:%S'))

    import mokuai
    # 反射模块的属性

    # 普通方法
    print(mokuai.day)
    # getattr方法
    print(getattr(mokuai,'day'))

    #反射模块的方法
    print(getattr(mokuai,'func')())

    import sys
    def func():
        print('反射自己模块的变量')

    name = 'felix'
    print(sys.modules)  #所有引用的模块
    print(sys.modules['__main__'])  #当前模块
    print(sys.modules['__main__'].name) #  #当前模块的变量 name

    # 反射自己模块中的变量
    print(getattr(sys.modules['__main__'],'name'))

    # 反射自己模块中的方法(函数)
    getattr(sys.modules['__main__'],'func')()
    getattr(sys.modules[__name__],'func')()

    # x = input('>>>')
    # print(getattr(sys.modules['__main__'],x))

  

# setattr()  设置一个变量
class A:
    pass
a = A()
setattr(A,'name','felix')  #设置类的属性
setattr(a,'name','alina')  #设置对象的属性
print(A.name) 
print(a.name)
felix
alina # delattr() 删除一个变量 delattr(a,'name') #删除对象的属性 a就去找类的属性 为 felix print(a.name) felix delattr(A,'name') print(a.name) 报错: AttributeError: 'A' object has no attribute 'name'

  

# setattr()  设置一个变量
class A:
    pass
a = A()
setattr(A,'name','felix')  #设置类的属性
setattr(a,'name','alina')  #设置对象的属性
print(A.name) 
print(a.name)
felix
alina

# delattr()  删除一个变量
delattr(a,'name') #删除对象的属性 a就去找类的属性 为 felix
print(a.name)
    felix
 
delattr(A,'name')
print(a.name)
    报错:
AttributeError: 'A' object has no attribute 'name'

 

八、类的内置方法(专有方法)

1.__str__  --->>>       str(obj)

2.__repr__  --->>>  repr(obj)

class Teacher:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def __str__(self):
        return "Teacher's object :%s" % self.name

    def __repr__(self):
        return str(self.__dict__)

    def func(self):
        return 'wahaha'

felix = Teacher('felix', 8888)
print(felix)  # 打印一个对象的时候,就是调用a.__str__
print(str(felix))
print(repr(felix))
print('>>> %r' % felix)
print(felix.__str__)
执行结果:
Teacher's object :felix
Teacher's object :felix
{'name': 'felix', 'salary': 8888}
>>> {'name': 'felix', 'salary': 8888}
<bound method Teacher.__str__ of {'name': 'felix', 'salary': 8888}>

 print(felix.__str__)

object  里有一个__str__,一旦被调用,就返回调用这个方法的对象的内存地址

%s str() 直接打印 实际上都是走的__str__
%r repr() 实际上都是走的__repr__

repr 是str的备胎,但str不能做repr的备胎

小结:
1.print(obj) / '%s'%obj / str(obj)的时候,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串
如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__。
2.repr(),只会找__repr__,如果没有就找父类的

3.__len__  --->>>        len()

class Classes:
    def __init__(self,name):
        self.name = name
        self.student = []
    def __len__(self):
        return len(self.student)

python = Classes('Python3')
python.student.append('Felix')
python.student.append('Alina')
print(len(python))
执行结果:
2

4.__del__      --->>>       析构函数

class A:
    def __del__(self):  # 析构函数: 在删除一个对象之前进行一些收尾工作
        self.f.close()

a = A()
a.f = open('文件名')  # 打开文件 第一 在操作系统中打开了一个文件 拿到了文件操作符存在了内存中
del a  # a.f 拿到了文件操作符消失在了内存中
del a  # del 既执行了这个方法,又删除了变量

  

5.__call__      --->>>       obj(obj) 

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

    def __call__(self):
        for k in self.__dict__:
            print(k, self.__dict__[k])

a = A('felix', 18)()
执行结果:
name felix
age 18

 

6.item

1.__getitem__
2.__setitem__
3.__delitem__

实例:
class Teacher:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __getitem__(self, item):
        return self.__dict__[item]

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __delitem__(self, key):
        # del self.__dict__[key]
        self.__dict__.pop(key)

    def __delattr__(self, item):  #
        self.__dict__.pop(item)

felix = Teacher('felix',18)
print(felix.__dict__['name'])

felix.__dict__['sex'] = 'boy'
print(felix.sex,felix.__dict__['sex'])
print(felix.__dict__)

# del felix.age  #object里的方法  原生支持 执行__delattr__方法 没有写__delattr__也不会报错
                 # 所以当没有__delitem__方法也不会报错 继承object
del felix['age']  #当没有实现__delitem__方法会报错
# print(felix.__dict__['age'])   #会报错
print(felix.__dict__)

执行结果:
felix
boy boy
{'name': 'felix', 'age': 18, 'sex': 'boy'}
{'name': 'felix', 'sex': 'boy'}

  

  

  

  

posted @ 2018-10-14 23:01  %华&仔%  阅读(247)  评论(0编辑  收藏  举报