Please enable Javascript to view the contents

 ·  ☕ 12 分钟 · 👀... 阅读

面向对象

面向对象基础

​ 在用面向对象的时候一般类名用大写字母开头,函数名用小写字母开头

1
2
3
def FunctionName(args):
    '函数文档字符串'
    函数体

实例化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Person:   #定义一个人类

    role = 'person'  #人的角色属性都是人

    def __init__(self,name):
        self.name = name  # 每一个角色都有自己的昵称;

        
    def walk(self):  #人都可以走路,也就是有一个走路方法

        print("person is walking...")

print(Person.role)  #查看人的role属性

print(Person.walk)  #引用人的走路方法,注意,这里不是在调用

语法:对象名 = 类型(参数)

查看属性&调用方法

1
2
print(对象名.属性名) #查看属性

print(对象名.方法名) #调用方法

类的补充

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
一:我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名value为属性值

二:特殊的类属性
类名.__name__# 类的名字(字符串)

类名.__doc__# 类的文档字符串

类名.__base__# 类的第一个父类(在讲继承时会讲)

类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)

类名.__dict__# 类的字典属性

类名.__module__# 类定义所在的模块

类名.__class__# 实例对应的类(仅新式类中)

小节

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class 类名:

	def __init__(self,参数1,参数2)
        self.对象的属性1 = 参数1
        self.对象的属性2 = 参数2
        
    def 方法名(self):pass

    def 方法名2(self):pass
    
    
对象名 = 类名(1,2)  #对象就是实例,代表一个具体的东西

                  #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法

                  #括号里传参数,参数不需要传self,其他与init中的形参一一对应

                  #结果返回一个对象

对象名.对象的属性1   #查看对象的属性,直接用 对象名.属性名 即可

对象名.方法名()     #调用类中的方法,直接用 对象名.方法名() 即可

对象之间的交互

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person:
    role = 'person'
    def __init(self,name,aggressivity,life_value):
        self.name = name
        self.aggressivity = aggressivity
        self.life_value = life_value
     def attack(self,dog):
        dog.life_value -= self.aggressivity
 
class Dog:
    role = 'dog'
    def __init(self,name,breed,aggressivity,life_value):
        self.name = name
        self.breed = breed
        self.aggressivity = aggressivity
        self.life_value = life_value
    def bite(self,people)
    	people.life_value -= self.aggressivity
        
egg = Person('agon',10,1000)
ha2 = Dog('erlengzi','哈士奇'10,1000)
print(ha2.life.value)
egg.attack(ha2)
print(ha2.life.value)
再来一个例子
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from math import pi

class Circle:
    '''
    定义了一个圆形类;
    提供计算面积(area)和周长(perimeter)的方法
    '''
    def __init__(self,radius):
        self.radius = radius

    def area(self):
         return pi * self.radius * self.radius

    def perimeter(self):
        return 2 * pi *self.radius


circle =  Circle(10) #实例化一个圆

area1 = circle.area() #计算圆面积

per1 = circle.perimeter() #计算圆周长

print(area1,per1) #打印圆面积和周长

面向对象的三大特征

继承

what is 继承:儿子有了父亲,父亲有了爷爷,反过来就是继承

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#继承的代码实现

class Animal:

    def eat(self):
        print("%s 吃 " %self.name)

    def drink(self):
        print ("%s 喝 " %self.name)

class Cat(Animal):

    def __init__(self, name):
        self.name = name
        self.breed = '猫'

    def climb(self):
        print('爬树')

class Dog(Animal):

    def __init__(self, name):
        self.name = name
        self.breed='狗'

    def look_after_house(self):
        print('汪汪叫')


# ######### 执行 #########


c1 = Cat('小白家的小黑猫')
c1.eat()

c2 = Cat('小黑的小白猫')
c2.drink()

d1 = Dog('胖子家的小瘦狗')
d1.eat()

​ 提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大生了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大.

派生

​ 当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Animal:
    '''
    人和狗都是动物,所以创造一个Animal基类
    '''
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 人和狗都有自己的昵称;

        self.aggressivity = aggressivity  # 人和狗都有自己的攻击力;

        self.life_value = life_value  # 人和狗都有自己的生命值;


    def eat(self):
        print('%s is eating'%self.name)

class Dog(Animal):
    '''
    狗类,继承Animal类
    '''
    def bite(self, people):
        '''
        派生:狗有咬人的技能
        :param people:  
        '''
        people.life_value -= self.aggressivity

class Person(Animal):
    '''
    人类,继承Animal
    '''
    def attack(self, dog):
        '''
        派生:人有攻击的技能
        :param dog: 
        '''
        dog.life_value -= self.aggressivity

egg = Person('egon',10,1000)
ha2 = Dog('二愣子',50,1000)
print(ha2.life_value)
print(egg.attack(ha2))
print(ha2.life_value)
super

在python3中,子类执行父类的方法也可以直接用super方法.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16

class A:
    def hahaha(self):
        print('A')

class B(A):
    def hahaha(self):
        super().hahaha()
        #super(B,self).hahaha()

        #A.hahaha(self)

        print('B')

a = A()
b = B()
b.hahaha()
super(B,b).hahaha()
接口类

继承有两种用途:

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

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

借用abc模块来实现接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from abc import ABCMeta,abstractmethod

class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self,money):
        pass


class Wechatpay(Payment):
    def fuqian(self,money):
        print('微信支付了%s元'%money)

p = Wechatpay() #不调就报错了

抽象类
  • 抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
  • 抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
  • 在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。
钻石继承

img

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class A(object):
    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):
    # def test(self):

    #     print('from F')

    pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性


#新式类继承顺序:F->D->B->E->C->A

#经典类继承顺序:F->D->B->A->E->C

#python3中统一都是新式类

#pyhon2中才分新式类与经典类

查看继承顺序用、:对象名.mro 来查看

继承小结
  • 继承的作用

  • 减少代码的重用

  • 提高代码可读性

  • 规范编程模式

  • 几个名词

    • 1
      2
      3
      
      抽象:抽象即抽取类似或者说比较像的部分。是一个从具题到抽象的过程。
      继承:子类继承了父类的方法和属性
      派生:子类在父类方法和属性的基础上产生了新的方法和属性
      
  • 抽象类与接口类

  • 1.多继承问题
    在继承抽象类的过程中,我们应该尽量避免多继承;
    而在继承接口的时候,我们反而鼓励你来多继承接口
    
  • 2.方法的实现
    在抽象类中,我们可以对一些抽象方法做出基础实现;
    而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
    

封装

  • 作用
    • 1、将变化隔离;
    • 2、便于使用;
    • 3、提高复用性
    • 4、提高安全性
    • 封装原则
      • 将不需要对外提供的内容都隐藏起来;
      • 把属性都隐藏,提供公共方法对其访问。
私有变量
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#其实这仅仅这是一种变形操作

#类中所有双下划线开头的名称如__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 A:
...     def fa(self):
...         print('from A')
...     def test(self):
...         self.fa()
... 
>>> class B(A):
...     def fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from B
 

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

什么是特性property

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

你胖吗?

过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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)

为什么要用property

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

除此之外,看下

1
2
3
4
5
6
7
ps:面向对象的封装有三种方式:
public
这种其实就是不封装,是对外公开的
protected
这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
private
这种封装对谁都不公开

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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'
del f1.AAA
classmethod
1
2
3
4
5
6
7
class Classmethod_Demo():
    role = 'dog'

    @classmethod
    def func(cls):
        print(cls.role)
Classmethod_Demo.func()
staticmethod
1
2
3
4
5
6
7
class Staticmethod_Demo():
    role = 'dog'

    @staticmethod
    def func():
        print("当普通方法用")
Staticmethod_Demo.func()

多态

  • 多态指的是一类事物有多种形态

    ​ 动物有多种形态:人,狗,猪

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物

    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #动物的形态之一:人

    def talk(self):
        print('say hello')

class Dog(Animal): #动物的形态之二:狗

    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪

    def talk(self):
        print('say aoao')
多态性
1
2
3
4
5
	在面向对象方法中一般是这样表述多态性:
	向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。
也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

	比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

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

#peo、dog、pig都是动物,只要是动物肯定有talk方法

#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用

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

#更进一步,我们可以定义一个统一的接口来使用

def func(obj):
    obj.talk()
鸭子类型

逗比时刻:

  Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’

python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象

也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。

例1:利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法

例2:序列类型有多种形态:字符串,列表,元组,但他们直接没有直接的继承关系

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用

class TxtFile:
    def read(self):
        pass

    def write(self):
        pass

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

面向对象进阶

isinstance和issubclass

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

1
2
3
4
5
6
class Foo(object):
     pass
  
obj = Foo()
  
isinstance(obj, Foo)

issubclass(sub, super)检查sub类是否是 super 类的派生类

class Foo(object):
    pass
 
class Bar(Foo):
    pass
 
issubclass(Bar, Foo)

反射

what is 反射??? 主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。

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

四个可以实现自省的函数

下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

1
callable()判断变量是否可以调用

hasattr(对象名,方法名)判断对象是不是一个可执行的方法(函数)

getattr(对象名,方法名)执行方法

str__和__repr

改变对象的字符串显示__str__,__repr__

item系列

getitem_setitem___delitem__

__del__

析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
del f1
print('------->')

#输出结果

执行我啦
------->

__new__

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Person(object):

    def __new__(cls, name, age):
        print '__new__ called.'
        return super(Person, cls).__new__(cls, name, age)

    def __init__(self, name, age):
        print '__init__ called.'
        self.name = name
        self.age = age

    def __str__(self):
        return '<Person: %s(%s)>' % (self.name, self.age)

if __name__ == '__main__':
    name = Person('xxx', 24)
    print name

当实例化的时候,结果:

1
2
3
__new__ called.
__init__ called.
<Person: xxx(24)>

__call__

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

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print('__call__')


obj = Foo() # 执行 __init__

obj()       # 执行 __call__

with和__enter__,__exit__

__len__

1
2
3
4
5
6
7
8
9
class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __len__(self):
        return len(self.__dict__)
a = A()
print(len(a))

__hash__

1
2
3
4
5
6
7
8
9
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))

__eq__

面试题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __hash__(self):
        return hash(self.name+self.sex)

    def __eq__(self, other):
        if self.name == other.name and self.sex == other.sex:return True


p_lst = []
for i in range(84):
    p_lst.append(Person('egon',i,'male'))

print(p_lst)
print(set(p_lst))
分享

幽梦
作者
幽梦
傻猪男孩

目录