python-面向对象

1.面向过程

     -----什么叫做面向过程

      面向过程:处理事物的步骤、流程。  核心:过程

       优点:降低程序的复杂度          缺点:可扩展性差

2.面向对象

先记面向对象的三大特性:

1,封装     2,继承      3,多态(python中处处是多态,本身就带多态)

    ------面向对象:一切皆对象       核心:对象

      优点:解决了程序可扩展性差的问题

注:在python中变量表示特征,由函数表示技能,因而类是变量与与函数的结合体,对象是变量与方法(指向类的函数)的结合体

3.类

     类的概念:分类,共同特征与否判断一类还是不是一类,类是提取相同的特征

    在现实生活中:先有对象再有类,在程序中则是先有类,再有对象

定义类:就是定义变量与函数     首字母大写

***由一个抽象的东西加括号后跟冒号就产生一个类,返回值叫实例化对象,实例化触发__init__执行,__init__方法不能有返回值,实例化尽量不要调用类属性

class Person:   #定义一个人类      
    role='中国人'   #     类属性    静态属性   
    def __init__(self,name,life_value,aggr):  #__init__方法     动态属性
        self.name=name         #姓名   动态属性
        self.life_value=life_value   #生命值    动态属性
        self.aggr=aggr       #攻击力    动态属性
    def attack(self,enemy):      #定义了一个攻击的类属性方法
        enemy.life_value=enemy.life_value-self.aggr    #对象的生命值
        print('攻击')
egg=Person('egon',1000,50)     #实例化这个对象    就等于执行Person.__init__()
print(egg.name)     #查看属性/调用方法        
print(egg.life_value)
print(egg.aggr)

实例化的过程就是类------对象的过程
self-----自动将实例化对象/实例本身传给__init__的第一个参数

程序之间的交互

class Person:
    role='中国人'     # 类属性     静态属性
    def __init__(self,name,life_value,aggr):     #__init__方法   动态属性
#动态属性 self.name
=name #姓名
self.life_value
=life_value #生命值 self.aggr=aggr #攻击力 def attack(self,alex): #定义一个攻击的动态属性 alex.life_value-=self.aggr #alex的生命值 #egon.life_value-=self.aggr #egon的生命值
 # 实例化对象    查看属性/调用方法 egon
=Person('geon',1000,900) alex=Person('alex',500,100) print(egon.life_value) #egon未战斗之前的生命值 alex.attack(egon) #alex攻击egon print(egon.life_value) #egon战斗之后的生命值 print(alex.life_value) #alex未战斗之前的生命值 egon.attack(alex) #egon攻击alex print(alex.life_value) #alex战之后的生命值 class Dog: #定义狗类 def __init__(self,name,life_value,aggr): #__init__方法 动态属性
#动态属性 self.name
=name #名字 self.life_value=life_value #生命值 self.aggr=aggr #攻击力 def bite(self,Person): #定义狗咬的技能 egon.life_value-=self.aggr #egon的被攻击后的生命值 旺财=Dog('小强',500,200) #实例化dog对象 # #egon与旺财大战 print(egon.life_value) #狗未攻击egon之前egon的生命值 旺财.bite(egon) #狗攻击egon print(egon.life_value) #egon被攻击之后的生命值 print(旺财.life_value) #egon未打狗之前狗的生命值 egon.attack(旺财) ##egon打狗 print(旺财.life_value) #狗被打之后的生命值 #alex与旺财大战 print(alex.life_value) #alex没被狗攻击之前的生命值 旺财.bite(alex) #狗攻击alex print(alex.life_value) #alex被咬之后的生命值 print(旺财.life_value) #狗没挨打之前的生命值 alex.attack(旺财) #alex打狗 print(旺财.life_value) #狗受伤之后的生命值


4.类命名空间、实例命名空间

   创建一个类就会创建一类命名空间,用来存储类中定义所有名字,这些名字称为类名属性

而类有两种属性:静态属性和动态属性

  • 静态属性就是直接在类中定义的变量
  • 动态属性就是定义在类中的方法
# #面向对象名称空间问题
class Person:
role=‘中国人’
pass egon.role
='印度人' print(egon.role,id(egon.role)) print(alex.role,id(alex.role)) print(Person.role,id(Person.role)) Person.role='印度人' print(egon.role,id(egon.role)) print(alex.role,id(alex.role)) print(Person.role,id(Person.role)) # #总结:在面向对象中,如果要调用一个属性,自己没有就会在类中找,如果自己有,就调用本身的

5.面向对象的组合与用法

 组合用法:在一个类中以另一个类的对象作为数据属性,这个类就称为类组合

示例

class 爷爷:
    def __init__(self,姓名,年龄,工作):
        self.姓名=姓名
        self.年龄=年龄
        self.工作=工作

class 爸爸:
    def __init__(self,姓名,年龄,工作):
        self.姓名=姓名
        self.年龄=年龄
        self.工作=工作

class 儿子:
    def __init__(self,姓名,年龄,工作,爷爷_姓名,爷爷_年龄,爷爷_工作):
        self.姓名=姓名
        self.年龄=年龄
        self.工作=工作
        self.爷爷=爷爷(爷爷_姓名,爷爷_年龄,爷爷_工作)
小强=儿子('小强',15,'学生','霸刀',87,'农民')
print(小强.爷爷.姓名,小强.爷爷.年龄,小强.爷爷.工作)
小强=爸爸('大强',50,'农民')
print(小强.姓名,小强.年龄,小强.工作)

圆环实例

from math import pi
class Circle:
    def __init__(self,r):    #动态参数
        self.r=r    #名字    对象属性

    def area(self):        #定义面积方法      动态属性
        return pi * self.r**2     #计算面积并返回

    def perimeter(self):     #定义周长方法    动态属性
        return pi * self.r*2    #计算周长并返回
a=Circle(10)    #实例化一个圆对象
print(a.area())
print(a.perimeter())
class Ring:    #
    def __init__(self,r1,r2,p=3.14):
        self.r1_circle=Circle(r1)
        self.r2_circle=Circle(r2)
        self.p=p
    def area(self):
        return self.r1_circle.area()-self.r2_circle.area()
    def  perimeter(self):
        return self.r1_circle.perimeter()+self.r2_circle.perimeter()
a=Ring(10,5)
print(a.area())
print(a.perimeter())

 

 

6.面向对象的继承
   什么叫继承

继承:什么   是  什么的关系(人和狗是动物等)

     继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类

在python中继承分为单继承和多继承

 单继承就是在子类中继承一个父类,

多继承就是在子类中继承二个或多个父类

继承的作用:减少代码的重用,提高代码的可读性,规范代码编写模式。

如下例子:

#A,B,C为定义的三个父类/基类/超类
class A:
    pass
class B:
    pass
class C:
    pass
# a为子类(也叫派生类)
class a(A):pass    #看吧---这叫什么----这就叫单继承
# b 为子类(派生类)
class b(A,B,C):pass  #在看多继承。好好看

查看继承

#查看继承(__base__和__bases__)
#   base------从左至右查看第一个父类
print(b.__base__)
#   bases-----查看所有的父类
print(b.__bases__)   
#注意:如果在没有指定基类/父类时,python默认继承object类,因为python中一切皆对象
print(A.__bases__)

重点来了:object是所有python类的基类

 

继承与抽象

抽象

1,抽象----就是将对象比较像的部分抽取成类,

2,将类比较像的部分抽取成父类

抽象最主要的部分是划分类别(各类关注点,降低复杂度)

继承:

基于抽象的结果,通过编程语言的实现,先经过抽象这个过程,在通过继承的方式表达出抽象的结构

 

继承与重用

#继承与重用性
# ================第一部分
#定义两个类,类之间有很多相同的部分
# 如定义猫和狗类
# 猫可以:喵喵叫,吃,喝,睡
# 狗可以:汪汪叫,吃,喝,睡
class Cat:   #定义一个猫类
    def mews(self):   #在类里面定义方法
        print('喵喵叫')
    def eat(self):    #定义方法
        print('')
    def sleep(self):  #定义方法
        print('')

class Dog:
    def bark(self):
        print('汪汪叫')
    def eat(self):
        print('')
    def sleep(self):
        print('')

#由上:猫和狗的多有属性相同,且猫和狗都是动物
#继承思想铺路
class Animal:   #定义动物类(父类)
    def eat(self):
        print('')
    def sleep(self):
        print('')

class Cat(Animal):    #猫类继承动物类(子继父类)子类
    def mews(self):   #定义独有的方法
        print('喵喵叫')    #
class Dog(Animal):    #狗类继承动物类(子继父类)子类
    def bark(self):   #定义独有的方法
        print('汪汪叫')

#罢了,继承做它
class Animal:    #父/基/超类
    def eat(self):   #定义吃方法
        print('%s 吃'%self.name)
    def sleep(self): #定义睡方法
        print('%s 睡'%self.name)

class Cat(Animal):   #子类(派生类)
    def __init__(self,name):    #初始化
        self.name = name   #动态属性
        self.breed = ''  #派生属性(独有属性)
    def cry(self):   #定义cry方法(派生方法)
        print('喵喵叫')

class Dog(Animal):  #定义狗类(派生类)
    def __init__(self,name):   #初始化
        self.name = name       #动态属性
        self.breed = ''     # 派生属性(独有属性)
    def cry(self):            #定义派生方法
        print('汪汪叫')
#执行
#实例化(生成对象)
c1 = Cat('小黑')
print(c1)
print(c1.eat())
d1 = Dog('小强')
print(d1.cry())



#例题

class
Animal: '人和狗都是动物,所以创造一个Animal基类'
   代码重用
def __init__(self,name,agg,life_value): #初始化 self.name = name #人和狗都有自己的名字 self.aggr = agg #人和狗都有自己的攻击力 self.life_value = life_value #人和狗都有自己生命值 def eat(self): #定义eat方法 print('%s is eating'%self.name) class Dog(Animal): #定义派生类 pass class Person(Animal): #定义派生类 pass #实例化对象 egg = Person('egon',10,100) #人(对象) ha2 = Dog('二狗',20,50) #狗(对象) egg.eat() #子类对象调用父类方法 ha2.eat() #子类对象调用父类方法

******子类可以继承父类同时也可以定义自己的属性---派生属性(也叫独有的属性),直接不影响父类若定义的属性与父类的重名,那么调用新增的属性时以自己为准(调用的属性在子类中有就调用在子类中的,子类中没有就调用父类中的)

注意注意:子类能调用父类的属性,父类不能调用子类的属性

那么派生来了

 

派生

#派生:子类继承父类就叫做派生
class Animal:  #父类/基类/超类
    '人和狗都是动物,所以创造有个动物类(父类/基类/超类)'
    def __init__(self,name,aggr,life_value):
        self.name = name    #每个角色都有自己的名字
        self.agg = aggr     #每个角色都有自己的攻击力
        self.life_value = life_value     #每个角色都有自己的生命值

    def eat(self):   # 父类中定义一个共享方法
        print('%s is eating'%self.name)

class Person(Animal):    #子类/派生类
    '人类,继承动物类'
    def attack(self,Dog):   #派生方法
        '派生,人打狗的技能'
        Dog.life_value -= self.agg
        #狗的生命值等于狗的生命值减掉人的攻击(人打狗,狗掉血)
        
class Dog(Animal):    #子类/派生类
    '狗类,继承也继承了Animal类'
    def bite(self,Person):    #派生方法
        '派生,狗攻击人的技能'
        Person.life_value -= self.agg   
        #人的生命值等于人的生命值减掉狗的攻击(狗咬人,人掉血)

#实例化
egg = Person('麦兜',500,1000)   #实例化一个人对象
dog = Dog('毛发',250,1200)    #实例化一个狗对象

print(egg.life_value)    #查看人的生命值
print(dog.agg)     #查看狗的攻击力

egg.attack(dog)   #人打狗
print(dog.life_value)  #查看被人打了之后狗的生命值

dog.bite(egg)    #狗咬人
print(egg.life_value)   #查看被狗咬过之后人的生命值

 又来注意:当对象调用类属性时,先从实例化中找,在到子类中找,最后到父类中找。直至最顶层一个类

 来尝例题:

# 人 狗 相同属性的同时 还有一些不同的属性
#人,狗,大战,且人有武器的游戏
# class Animal:
#     def __init__(self,aggressivity, life_value,name):
#         self.name = name  # 每一个角色都有自己的昵称;
#         self.aggressivity = aggressivity  # 每一个角色都有自己的攻击力;
#         self.life_value = life_value  # 每一个角色都有自己的生命值;
#     def eat(self):
#         self.life_value += 10
#
# class Person(Animal):
#     def __init__(self, name, aggressivity, life_value, money):
#         Animal.__init__(self, name, aggressivity, life_value)
#         self.money = money    #派生属性:父类没有的属性
#
#     def attack(self,dog):
#         dog.life_value -= self.aggressivity
#
#     def get_weapon(self,weapon_obj):
#         if self.money > weapon_obj.price:
#             self.money -= weapon_obj.price  # 金老板花钱买武器
#             self.weapon = weapon_obj  # 金老板装备打狗棒
#             self.aggressivity += weapon_obj.aggr  # 金老板的攻击力增加了
# class Dog(Animal):
#     def __init__(self, name, breed, aggressivity, life_value):
#         Animal.__init__(self,aggressivity,life_value,name)
#         self.breed = breed  # 每一只狗都有自己的品种; #派生属性:父类没有的属性
#
#     def bite(self,people):  # 派生方法 :父类没有的方法
#         people.life_value -= self.aggressivity
#
#     def eat(self):
#         Animal.eat(self)
#         print('dog is eating')

# snoopy = Dog('太白','京巴',250,500)
# print(snoopy.breed)
# print(snoopy.name)
# # Animal.eat(snoopy)
# snoopy.eat()
# print(snoopy.life_value)
# snoopy.eat()
# print(snoopy.life_value)

派生属性:在自己的__init__方法里使用父类的__init__方法--------指名道姓调用方法

派生方法:在子类中增加父类没有的

父类没有子类有:找子类

子类没有父类有:找父类

子类有,父类也有:先找子类,再找父类

子类有,父类有,只想用父类的:父类名调用自己的方法(对象)

子类有,父类有,子类父类都想用,在子类中指名道姓的调用父类的方法

 总结:子类有的就用子类的,只要想用父类的就父类名.父类方法(子类对象)

 

 

****在新式类中有super()方法

super()-------方法可以找父类的方法

# 在新式类
class Animal:
    def __init__(self,aggressivity, life_value,name):
        self.name = name  # 每一个角色都有自己的昵称;
        self.aggressivity = aggressivity  # 每一个角色都有自己的攻击力;
        self.life_value = life_value  # 每一个角色都有自己的生命值;
    def eat(self):
        self.life_value += 10

class Person(Animal):
    def __init__(self, name, aggressivity, life_value, money):
        # Animal.__init__(self, name, aggressivity, life_value)
        super().__init__(name, aggressivity, life_value)  #新式类
        self.money = money    #派生属性:父类没有的属性

    def attack(self,dog):
        dog.life_value -= self.aggressivity

    def get_weapon(self,weapon_obj):
        if self.money > weapon_obj.price:
            self.money -= weapon_obj.price  # 金老板花钱买武器
            self.weapon = weapon_obj  # 金老板装备打狗棒
            self.aggressivity += weapon_obj.aggr  # 金老板的攻击力增加了
class Dog(Animal):
    def __init__(self, name, breed, aggressivity, life_value):
        # Animal.__init__(self,aggressivity,life_value,name)
        # super(Dog,self).__init__(aggressivity,life_value,name)
        super().__init__(aggressivity,life_value,name)
        self.breed = breed  # 每一只狗都有自己的品种; #派生属性:父类没有的属性

    def bite(self,people):  # 派生方法 :父类没有的方法
        people.life_value -= self.aggressivity

    def eat(self):
        # Animal.eat(self)
        super().eat()
        print('dog is eating')
snoopy = Dog('太白','京巴',250,500)
print(snoopy.breed)
print(snoopy.name)
snoopy.eat()
print(snoopy.life_value)
super(Dog,snoopy).eat()   #Animal.eat(snoopy)
print(snoopy.life_value)

# 用子类的对象,调用父类的方法:
# 如果子类中没有这个方法,直接就使用父类的
# 如果子类中有同名方法:
#     经典类 指名道姓 类名.方法名(子类对象) 类内外一致
#     新式类 super方法 super(子类名,子类对象).方法名() 类内可以省略super的参数

总结一下下:在新式类中,super()方法其实是替换了父类名调用__init__方法,但是不同于父类名调用__init__方法的是:如果定义了多个父类,super使用父类的方法时找最近的那个父类,如果在多个父类的情况下要使用任意一个类的方法,就要指名道姓的调用使用。。。。。。

 如:super().__init__(name,aggr,life_value)

记得super加括号调用__init__方法(self不用写了,只传入属性)

 

钻石继承

钻石继承是经典类和新式类的多继承问题,继承顺序问题

来看经典类先,(注意,经典类只有python2版本中有,python3中只有新式类)

******D是B和C的父类,B和C是A的父类。

重要非常-----python2与python3中的异同:

python2中有经典类和新式类,python3中只有新式类

#经典类
class B:
    pass
class C(B):
    pass

#新式类
class B(object):
    pass
class C(B):
    pass

 

 

上图经典类(深度优先)和新式类(广度优先)查找顺序

深度优先查找顺序:A----->B------>D-------->F------>C-------->E

广度优先查找顺序:A----->B------>D------->C------->E-------->F

深度优先为经典类,广度优先为新式类

经典类例题,以深度优先查找

class F():
    def f(self):
        print('F')
class E(F):
    def f(self):
        print('E')

class D(F):pass
    # def f(self):
    #     print('D')

class B(D):pass
    # def f(self):
    #     print('B')
class C(E):
    def f(self):
        print('C')
class A(B,C):pass
    # def f(self):
    #     print('A')
a = A()
a.f()

新式类示例,以广度优先查找,

class F(object):   ---------在python2版本中经典类与新式类的区别就在这
    def f(self):
        print('F')
class E(F):
    def f(self):
        print('E')

class D(F):pass
    # def f(self):
    #     print('D')

class B(D):pass
    # def f(self):
    #     print('B')
class C(E):
    def f(self):
        print('C')
class A(B,C):pass
    # def f(self):
    #     print('A')
a = A()
a.f()

 ******而在python3版本中默认都为新式类,没有经典类。也就是广度优先

 ------查看继承顺序  (第一个类名调用mro方法)

有两种方式(经典类中没有此方法):

#如上例题查找继承顺序    A.mro() == A.__mro__
print(A.mro())
print(A.__mro__)
# [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>]
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>)

来吧,看面试题

class Foo:
    def __init__(self):
        self.func()

    def func(self):
        print('Foo.func')
class Son(Foo):
    def func(self):
        print('Son.func')
s = Son()

对上题做做思路:首先在程序最后面生成的是Son对象,所以先执行Son类,但是Son为Foo的子类而且这里Son(Foo)是派生类,那么执行Foo代码,在func方法中打印'Foo.func',别急,再看派生类中,人家也有func派生方法func,恰好也有输出,所以结果是‘Son.func’

*****在继承中,子类有的方法就优先执行子类的,子类中没有的就执行父类的。

 

 

多态

首先说明:python中处处是多态。然而有些人却说python中没有多态,简直错误。

python本身自带多态

什么是多态:一类事物有多种形态

class Animal:
    pass

class person(Animal):
    def attack(self):
        pass

class dog(Animal):
    def attack(self):
        pass

def attack(obj):    #这个就是多态
    obj.attack()

d = dog()
p = person()
attack(d)   # d.attack()
attack(p)   #p.attack()

 

比如:人,狗,猫是动物的多种形态

import abc
class Animal(metaclass=abc.ABCMeta):  #同一类事物:动物
    @abc.abstractclassmethod
    def talk(self):
        pass
class Person(Animal):  #形态一    人
    def talk(self):
        print('say hello')
class Dog(Animal):    #形态二    狗
    def talk(self):
        print('say wangwang')
class Cat(Animal):    #形态三   猫
    def talk(self):
        print('say miaomiao')

多态性

什么是多态性-----在不考虑实例类型的情况下使用实例

# 多态性
#peo,dog,pig都是动物,所以都有talk方法
# peo = People()
# dog = Dog()
# pig = Pig()

# peo.talk()
# dog.talk()
# pig.talk()

 

鸭子类型(有相同类型,相同方法等特性)

看上起来像,走起路来像,样子像鸭子就是鸭子-----根据特征相同定义事物

如:列表,元组就是一对鸭子类型,都是序列,都有len方法

python崇尚鸭子类型

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

 

封装

  1、狭义上的封装:把属性和方法藏在类中(只能在类内部调用,不能在类外部调用).隐藏对象的属性和实现的细节值

class Dog:
    __res = 'dog'   #私有属静态属性   当变量名前加双下划线时,就是把属性藏起来    **注:属性可以私有,方法也可以私有
    def func(self):
        print(Dog.__res)  #python默认:_类名__属性名
        #在类的外面不能直接调用,
print(Dog._Dog.__res) #语法不支持,不能这样调用,在类内加上一层密码   _类名
d = Dog()
d.func()

  2、广义上的封装:把一些属性和方法放到类中本身就是一种封装 (只能通过类名调用方法和属性。不能直接调用), 

class Dog:
    ret = 'dog'
print(Dog.ret)

分装的好处:将变化隔离,提高复用性,便于使用,提高安全性

私有变量和私有方法

在python中用双划线开头的方式将属性隐藏起来(设置成私有的)

私有变量

#其实这仅仅这是一种变形操作
#类中所有双下划线开头的名称如_x都会自动形成:_类名__x的形式:
class A:
    _N = 0  #类中数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如_N,会变形为_A__N
    def __init__(self):
        self.__X = 10   #变形为self._A__X
    def __foo(self):  #变形为_A__foo访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形        
        print('from A')
    def bar(self):
        self.__foo()   #只有在类内部才可以通过__foo的形式访问到
#A._A__N是可以

这种自动变形的特点:

  1,类中定以的__x只能在内部使用,如self.__x,引用的就是变形的结果

  2,这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的

  3,在子类定义的__X不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即上下划线开头的属性在继承给子类时,子类时无法覆盖的

双下方法:

class Dog:
    __res = 'dog'   #私有属静态属性   当变量名前加双下划线时,就是把属性藏起来
    def __discount(self):   #私有方法
        print('in_func')
    def price(self):
        self.__discount()  #在内部悄悄地调用
# 不管是私有的属性,私有的方法都是私有的

注::
# 在类中可以定义私有属性,私有方法,定义在类里面的私有属性和私有方法只能在类中调用,
# 在类外也可以调用但不常用,麻烦,不推荐这样做

*/*定义一个私有变量,在类内可以直接调用,在类外不能直接调用,如果一定要用,必须在私有方法之前加上:_类名。

如:

print(Dog._Dog.__res)

在类外的名字可以通过__dict__方法就可以查看。

 

实际例子:

为什么要将属性封装起来:不像被外部调用

1,涉及安全      2,没必要 

class Room:
    def __init__(self,name,price,lenght,width):
        self.name = name
        self.price = price
        self.__lenght = lenght   #私有的对象属性
        self.__width = width    #私有属性
    def area(self):
        return self.__lenght*self.__width

house = Room('胜哥',100000,120,100)
print(house.area())
实际例子

 

私有的:私有的有静态属性,方法,对象属性,使用__名字的方法调用,保证在类内部可以调用,外部不行

当一个名字不想被外部使用也不想被自类继承,只想在内部调用的时候就定义私有的

#私有方法
#把fa定义成私有的,即__fa
class A:
    def __fa(self):    #在定义时就变形为_A__fa
        print('from A')
    def test(self):
        self.__fa()
class B(A):
    def __fa(self):   #只能与自己所在的类为准,即调用_A__fa
        print('from B')
b = B()
b.test()

 

property属性

什么是特性property

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

class People:
    def __init__(self,name,weight,height):
        self.name = name
        self.weight = weight
        self.height = height
    @property  #伪装属性
    def bmi(self):
        return self.weight / (self.height**2)
p1 = People('egon',75,1.85)
print(p1.bmi)
View Code
import math
class Circle:
    def __init__(self,radius):   #圆的半径radius
        self.radius = radius
    @property
    def area(self):
        return math.pi*self.radius**2   #计算面积
    @property
    def perimeter(self):
        return 2*math.pi*self.radius   #计算周长
c = Circle(10)
print(c.radius)
print(c.area)    #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
print(c.perimeter)   #同上
圆的周长和面积

为什么要用property

将一个类的函数定义成特性后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行一个函数然后计算出来,这种特性的使用方式遵循了统一访问的原则

ps:面向对象的封装有三种方式:

【public】----这种其实就是不分装,对外开放

【peotected】-----这种封装对外不公开,但对朋友或子类(儿子)公开

【peivate】-----这种封装对谁都不公开

python中通过property方法可以实现

class Foo:
    def __init__(self,val):
        self.__name = val   #将所有的数据属性都隐藏起来
    @property
    def name(self):
        return self.__name   #obj.name访问的是self.__name(这也是真实值的存放位置)
    @name.setter
    def name(self,value):
        if not isinstance(value,str):   #在设定值之前进行类型检查
            raise TypeError('%s must be str' %value)
        self.__name = value  #通过类型检查后,将值value存放到真实的位置self.__name
    @name.deleter
    def name(self):
        raise TypeError('Can not delete')
f = Foo('egon')
print(f.name)
f.name = 10   #抛出异常'TypeError: 10 must be str'
del f.name   #抛出异常'TypeError: 10 Can not delete'

一个静态属性property本质就是实现了get,set,delete三种方法

class Foo:
    @property
    def AAA(self):
        print('get的时候运行我啊')
    @AAA.setter
    def AAA(self,value):
        print('set的时候运行我啊')
    @AAA.deleter
    def AAA(self):
        print('delete的时候运行我啊')
#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1 = Foo()
f1.AAA
f1.AAA = 'aaa'
def f1.AAA
View Code
class Foo:
    def get_AAA(self):
        print('get的时候运行我啊')

    def set_AAA(self,value):
        print('set的时候运行我啊')

    def delete_AAA(self):
        print('delete的时候运行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
View Code
class Goods:
    def __init__(self):
        #原价
        self.original_price = 100
        #折扣
        self.discount = 0.8
    @property
    def price(self):
        #实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price
    @price.setter
    def price(self,value):
        self.original_price = value
    @price.deleter
    def price(self):
        del self.original_price
obj = Goods()
obj.price   #获取价格
obj.price = 200   #修改商品原价
print(obj.price)
del obj.price     #删除商品原价

classmethod

class Classmethod_Demo():
    role = 'dog'
    @classmethod
    def func(cls):
        print(cls.role)
Classmethod_Demo.func()

staticmethod

class Statimethod_Demo():
    role = 'dog'
    @staticmethod
    def func():
        print('当普通方法用')
Statimethod_Demo.func()

 

 

面向对象的进阶:

面向对象里面用到了函数的内置方法有:property,classmethod,staticmethod,supper,object,1,isinstance,issubclass    两者用于判断,返回结果不是布尔值

 isinstance(对象,类)判断这个对象是不是这个类或者这个类的子类的实例化

class Foo(object):     
    pass
obj = Foo()          #实例化对象
print(isinstance(obj,Foo))      #实例化的对象obj是不是Foo这个类的实例化




class A:pass    #定义A类/父类
class B(A):      #定义子类/派生类
    pass 
b = B()           实例化b对象
# print(isinstance(b,A))     #判断b这个对象是不是A这个类的实例化     True
# print(isinstance(a,object))     #判断a对象是不是object这类的实例化  报错,没有a对象

from collections import Iterable
print(isinstance([],Iterable)) #判断列表是不是可迭代对象

issubclass(派生类,父类)判断一个类是不是另一个类的派生类

class Foo:     #父类/基类/超类
    pass
class Boo(Foo):   #子继父类/派生类
    pass
print(issubclass(Boo,Foo))   #判断Boo类是不是Foo的派生类   True


class A:pass
class B(A):pass
print(issubclass(B,A))    #判断B是不是A的派生类   True
print(issubclass(A,B))    # A为父类,不是B的派生类     False
print(issubclass(A,object))  #A是object的派生类     object是所有类的父类,所有类且继承object,所以所有类是object的派生类     True
print(issubclass(B,object))  #B是obiect的派生类        True

 

2,反射

什么是反射--------程序可以访问,检测,修改它本身状态和行为的一种能力(自省)

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

反射的四个内置函数:

getattr-----------------------获取成员

hasattr----------------------检查成员

setattr-----------------------设置成员

delattr-----------------------删除成员

以上内置函数适用于类和对象(一切皆对象,类本身也是一个对象)

def getattr(object,name,default = None): #已知的getattr特殊属性
    '''
    getattr(object, name[, default]) -> value

    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
    When a default argument is given, it is returned when the attribute doesn't
    exist; without it, an exception is raised in that case.

    '''
    pass

def hasattr(*args,**kwargs): #real dignature unknown
    '''
    Return whether the object has an attribute with the given name.

    This is done by calling getattr(obj, name) and catching AttributeError.

    '''
    pass

def setattr(x,y,v):   #real signature unknown;renstored from_doc_
    '''
    Sets the named attribute on the given object to the specified value.

    setattr(x, 'y', v) is equivalent to ``x.y = v''

    '''
    pass

def delattr(x,y):  # real sugnature unknown; restored from _doct_
    '''
    Deletes the named arrtibute form given object.
    
    delattr(x,'y') is equivkent to 'del x.y'
    
    '''
    pass

 

class Foo(object):   #定义Foo类
    def __init__(self):
        self.name = 'abc'
    def func(self):   #定义func方法
        return 'ok'   #返回ok
obj = Foo()    #实例化对象

# 获取成员
ret = getattr(obj,'func')   #获取的是个对象
r = ret()
print(r)

# 检查/判断成员
ret = hasattr(obj,'func')   #因为有func方法所以返回True
print(ret)   #查看结果

#设置成员
print(obj.name)    #设置之前为'abc'
ret = setattr(obj,'name',19)
print(obj.name)     #设置之后为19

# 删除成员
print(obj.name)   #删除之前为19
ret = delattr(obj,'name')   #执行删除
print(obj.name)   #报错,证明删除成功
class Foo:
    f = '类的静态变量'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def say_hi(self):
        print('hi,%s'%self.name)
obj = Foo('egon',37)    #实例化对象

#获取属性
n = getattr(obj,'name')   #获取属性
print(n)       #获取对象
func = getattr(obj,'say_hi')    #获取属性
func()   #调用
print(getattr(obj,'aaaaaa','不存在啊'))

# # 检测是否含有某属性  返回布尔值
n = hasattr(obj,'name')   #判断
print(n)
n = hasattr(obj,'say_hi')
print(n)

# 设置属性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'sb')
print(obj.__dict__)
print(obj.show_name(obj))

# 删除属性
delattr(obj,'age')
delattr(obj,'show_name')
delattr(obj,'show_namelll')   #不存在,则报错

print(obj.__dict__)

类也是对象

# 类也是对象
class Foo(object):
    staticField = 'old boy'   #静态属性
    def __init__(self):     
        self.name = 'egon'  #定义egon属性
    def func(self):     #定义func方法
        return 'func'     #返回func

    @staticmethod     #语法糖
    def bar():    
        return 'bar'
print(getattr(Foo,'staticField'))
print(getattr(Foo,'func'))
print(getattr(Foo,'bar'))

反射当前模块

#反射当前模块成员
import sys
def s1():
    print('s1')

def s2():
    print('s2')

this_module = sys.modules[__name__]
hasattr(this_module,'s1')
getattr(this_module,'s2')

 

类的(双下)内置方法

__str__ 和 __repr__

__repr__是__str__的备胎

#__str__和__repr__
#改变对象的字符串显示__str__和__repr__
#自定制格式化字符串__format__

format_dict = {
    'nat':'{obj.name} - {obj.addr} - {obj.type}',  #学校名-学校地址-学校类型
    'tna':'{obj.type} - {obj.name} - {obj.addr}',  #学校类型-学校名:学校地址
    'tan':'{obj.type} - {obj.addr} - {obj.name}',  #学校类型/学校地址/学校名
}
class School:
    def __init__(self,name,addr,type):
        self.name = name
        self.addr = addr
        self.type = type

    def __repr__(self):
        return 'School(%s,%s)'%(self.name,self.addr)
    def __str__(self):
        return '(%s,%s)'%(self.name,self.addr)
    def __format__(self, format_spec):
        if not format_spec or format_spec not in format_dict:
            format_spec = 'nat'
        fmt = format_dict[format_spec]
        return fmt.format(obj = self)
s1 = School('oldboy1','北京','私立')
print('from repr:',repr(s1))
print('from srt:',str(s1))
print(s1)

'''
srt函数或者print函数---》obj.__srt__()
repr或者交互式解释器----》obj.__repr__()
如果__srt__没有被定义,那么就会使用__repr__来代替输出
注意:这两种方法的返回值必须是字符串,否则抛出异常
'''
print(format(s1,'nat'))
print(format(s1,'tna'))
print(format(s1,'tan'))
print(format(s1,'asfdasdffd'))
class A:
    pass
    def __str__(self):
        return 'A的对象'
    def __repr__(self):
        return 'repr:A的对象'
a = A()
print(a)  #本质调用的是__str__,如果没实现,就调用__repr__,在找不到就用object父类的
print(a.__str__())    #str却不能做repr的备胎
print(a.__repr__())   #repr是str的备胎
#打印一个对象的时候,实际上是调用了这个对象所在类的__str__方法,打印的是这个方法的返回值
print(str(a))
print(repr(a))
print('%s'%a)
print('%r'%a)

 

%s和%r 

# %s和%r
class B:
    def __str__(self):
        return 'str:class B'
    def __repr__(self):
        return 'repr:class B'
b = B()
print('%s'%b)
print('%r'%b)

__del__ 析构方法

删除的时候对相关的也要进行处理,就用析构方法

# __del__析构方法
# 析构方法,当对象在内存中被释放时,自动触发执行。无须定义
#先执行代码中的内容,再删除对象
#如果删掉这个对象,这个对象的其他附属附属内容也没有用了,就认为被回收掉了 # 简单例子
class Foo: def __del__(self): print('执行我啦') f1 = Foo() print(f1) del f1 print('----->')

 

item系列

关于item,对象访问如果是对象名[ ] 形式,是因为对象内部实现了item系列方法

# __getitem__\__setitem__\__delitem__
class Foo:
    def __init__(self,name):
        self.name = name
    def __getitem__(self, item):
        print(self.__dict__[item])
    def __setitem__(self, key, value):
        self.__dict__[key] = value
    def __delitem__(self, key):   等待del触发执行
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)
f1 = Foo('sb')
f1['age'] = 18   给f1添加一个属性
f1['age1'] = 19
del f1.age1      删除属性
del f1['age']
f1['name'] = 'alex'
print(f1.__dict__)

 

__new__方法是创造一个裸地对象,new方法和init方法的顺序是,先执行new方法,在执行init方法

class A:
    def __init__(self):
        self.x = 1
        print('in init function')
    def __new__(cls, *args, **kwargs):
        print('in new function')
        return object.__new__(A,*args,**kwargs)
a = A()
print(a.x)

单例模式
class Singleton:
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            cls._instance = object.__new__(cls, *args, **kw)     #创建一个新对象
        return cls._instance
 
one = Singleton()    # obj = object.__new__()
two = Singleton()

two.a = 3
print(one.a)
# 3
# one和two完全相同,可以用id(), ==, is检测
print(id(one))
# 29097904
print(id(two))
# 29097904
print(one == two)
# True
print(one is two)

 




__call__ 对象后面加括号,触发执行

 注:构造方法的执行时由创建对象触发的,对象 = 类名();而对于__call__方法的执行是由对象后加括号的,即:对象()或者类()()
class Foo:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        print('__call__')
obj = Foo()   #执行__init__
print(obj)
obj()         #执行__call__


约定:对象名() 类后面加括号就是调用这个库,这种方法只用于__call__
obj()()
对象后面的括号和__call__是约定相关关系

 

__len__

class A:
    def __init__(self):
        self.a = 1    定义属性a = 1
        self.b = 1 
        self.c = 1    这里定义几个属性长度就是几
    def __len__(self):     —__len__对应len方法
        return len(self.__dict__)
a = A()
print(len(a))

在内置函数len什么实质是调用__len__方法

__hash__

# __hash__
class A:
    def __init__(self):
        self.a = 1
        self.b = 2
    def __hash__(self):
        return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))    这里的hash和return---hash都等同于__hash__    hash调用内置的__hash __

__eq__

class A:
    def __init__(self):    
        self.a = 1
        self.b = 2
        def __eq__(self, obj):    这里的__eq__就是equar-----相等的意思
            if self.a == obj.a and self.b == obj.b:
                return True  #函数返回True表示等式成立。return False  print 永远不相等  
a = A() 
b = A()        主函数都没有,print则不相等
print(a == b)  这里相等是是因为return了True      判断两个数相等的时候是调用内置的__eq__方法

可以没有判断条件,只要返回True,等式成立

 

纸牌游戏来啦

#纸牌游戏
from collections import namedtuple
Card = namedtuple('Card',['rank','suit'])
class FranchDeck:
    ranks = [str(n) for n in range(2,11)] + list('JQKA')
    suits = ['红心','方板','梅花','黑桃']
    print(ranks)
    print(suits)
    def __init__(self):
        self._cards = [Card(rank,suit) for rank in FranchDeck.ranks
                       for suit in FranchDeck.suits]
        for rank in FranchDeck.ranks:     #每循环一次就拿到一张扑克牌,
            for suit in FranchDeck.suits:
                self._cards.append(Card(rank,suit))   #再将这张扑克牌添加到了列表里面去
    #   实现了抽牌功能
    def __len__(self):
        return len(self._cards)
    def __getitem__(self, item):
        return self._cards[item]

    #   实现洗牌功能
    def __setitem__(self, key, value):
        self._cards[key] = value


deck = FranchDeck()     #实例化  ,实现init里面的属性
print(deck._cards)
print(deck[0])
from random import choice
print(choice(deck))
print(choice(deck))

#实现洗牌
from random import shuffle
shuffle(deck)
print(deck._cards)

 

面试题

内置方法解决

# 一个类有100个对象,每个对象有三个属性:name,age,sex、
# 如果两个对象的name和sex完全一致
# 请对这100个对象进行去重
class Person:    #创建一个类
    def __init__(self,name,age,sex):   #定义三个属性
        self.name = name
        self.age = age
        self.sex = sex
    #两个的hash结果相等且a == b,才去重
    def __hash__(self):    #定义hash方法
        return hash(self.name+self.sex)   #两个字符串拼接起来取hash值
    def __eq__(self, other):    #定义equar方法,判断相同
        #判断自己的姓名,性别和self的姓名性别相同,返回True
        if self.name==other.name and self.sex == other.sex:return True
p_lst = []   #建立一个空列表
for i in range(100):    #实例化100各个对象
    p_lst.append(Person('egon',i,'male'))     #将这100个对象添加到列表中
print(p_lst)
print(set(p_lst))   #给100个对象去重

 

posted on 2017-08-13 14:03  一万年  阅读(294)  评论(0编辑  收藏  举报