面向过程VS面向对象

面向过程的程序设计的核心是“过程”-(流水线思维),过程即解决问题的步骤。面向过程的设计就好比精心设计好一条流水线,考虑周全,什么时候处理什么东西。

优点:极大地降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。

缺点:一套流水线或者流程只能用来解决一个问题,比如生成汽水的流水线无法生产汽车!代码牵一发动全身

应用场景:一旦完成很少改动的业务场景。比如:linux内核,git,Apache HttpServer 等。

面向对象的程序设计的核心是“对象”-(上帝思维)。要理解对象为何物,必须把直自己当成上帝,在上帝的眼里世间万物皆为对象,不存在的也可以创造出来。

优点;解决对了程序的扩展性。对某一个对象做修改,会立刻反应到整个体系中。比如对游戏中对一个人物特征和技能做修改就很容易。

缺点:可控性差。无法向面向过程的程序设计流水线式的可以精确的预测问题的处理流程和结果。面向对象的程序一旦开始就有对象之间的交互解决问题。即便是上帝也无法预测最终结果。于是我们经常会看到一个游戏里由于某个参数的修改导致一刀砍死3个人,这个游戏就失去了平衡。

应用场景:需求经常变化的软件,一般需求的变化都集中在用户层。互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

面向对象编程可以使程序的扩展和维护变得简单,并且可以大大提高程序的开发效率。另外,给予面向对象的程序可以使其他程序员更加容易理解你的代码逻辑,从而使团队开发变得更加从容

名词解释:

类,对象,实例,实例化

类:具有相同特征的一类事物。比如人类

对象/实例:具体的某一个事物。比如李小龙,周杰伦

实例化:创建一个类的实例的过程就叫实例化。

初识类和对象

python中一切皆为对象。类型的本质就是类。

实例化:类名+括号就是实例化。他会触发__init__函数的运行。可以用它来定制实例自己的特征

class Person:
    role="person" 
    def __init__(self): 
        print("调用了__init__ fun")
p1=Person()

 

 运行后打印出“调用了__init__ fun”

class Person: #定义了一个类
    role="person" #类属性
    def __init__(self,name):  #__init__ 构造函数
        self.name=name
    def walk(self):
        print("person is walking...")

print(Person.role) #打印类的属性
egg=Person("egon")#实例化一个对象 egg
egg.walk() #调用类中的方法 对象名+方法名()

print(egg.name) 查看对象的属性

我们定义的类属性到底存在哪里,可用这种方法查看 print(Person.__dict__)  查出的是一个字典,key为属性名,value为属性值

{'__module__': '__main__', 'role': 'person', '__init__': <function Person.__init__ at 0x02212DB0>, 'walk':..........}

关于self

self:就是实例化(对象)本身,如上例 self 指的就是egg 这个对象。

举个栗子1

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) #实例化一个圆 areal=circle.area()#计算圆的面积 per1=circle.perimeter()#计算圆的周长 print(areal,per1)#打印圆的周长和面积

 举个栗子2

class Employee:
    empcount=0
    def __init__(self,name,salary):
        self.name=name
        self.salary=salary
        Employee.empcount+=1
    def displaycount(self):
        print("Total Employee %d" %Employee.empcount)
    def displayEmployee(self):
        print("name:{0},salary:{1}".format(self.name,self.salary))
print(Employee.empcount)

emp1=Employee("zhangsan",1000)
print(emp1.empcount)
emp2=Employee("lisi",2000)

print(emp1.displaycount())
print(emp2.displaycount())

print(emp1.displayEmployee())

 

对象之间的交互

class Person: #定义了一个人类
    role="person" #人的角色属性都是人
    def __init__(self,name,aggre,life_value):
        self.name=name #每个角色都有自己的昵称
        self.aggre=aggre #每个角色都有自己的攻击力
        self.life_value=life_value #每个角色都有自己的生命值
    def attack(self,dog_obj):
      #人可以攻击狗,这里的狗是一个对象。
      #人攻击狗,狗的生命值就会根据人的攻击力而下降
dog_obj.life_value-=self.aggre class Dog: #定义了一个狗类 role="dog" #狗的角色属性是狗 def __init__(self,name,breed,aggre,life_value): self.name=name #每个狗都有自己的昵称 self.breed=breed#每个狗都有自己的品种 self.aggre=aggre #每个狗都有自己的攻击力 self.life_value=life_value #每个狗都有自己的生命值 def bite(self,people_obj):
      #狗可以咬人,这里的人是个一个对象
      #狗咬人,那人的生命值就会根据狗的攻击力而下降
people_obj.life_value-=self.aggre
egg=Person("egon",20,1000) #创造了一个是实实在在的人egg ha2=Dog("旺财","中华田园犬",10,500)#创造了一个实实在在的狗 ha2 egg.attack(ha2) #egg对象 打了一下ha2对象 print(ha2.life_value)#查看下ha2 旺财的生命值是否有下降 ha2.bite(egg) # 旺财咬了下egg print(egg.life_value)#查看下egg的生命值是否有下降
class Person:
    role="person"
    def __init__(self,name,aggre,life_value,money):
        self.name=name
        self.aggre=aggre
        self.life_value=life_value
        self.money=money
    def attack(self,dog_obj):
        dog_obj.life_value-=self.aggre
class Dog:
    role="dog"
    def __init__(self,name,breed,aggre,life_value):
        self.name=name
        self.breed=breed
        self.aggre=aggre
        self.life_value=life_value
    def bite(self,person_obj):
    #狗可以咬人,这里的人也是一个对象 person_obj.life_value-=self.aggre class Weapon: #定义一个武器类 def __init__(self,name,price,aggre,life_value): self.name=name self.price=price self.aggre=aggre self.life_value=life_value def update(self,person_obj): #person_obj 就是要装备上这个武器的那个对象:egg person_obj.money-=self.price person_obj.aggre+=self.aggre person_obj.life_value+=self.life_value def prick(self,obj): #该武器的主动技能 这里的obj可以是人的对象也可以是狗的对象,反正扎谁一下都掉血,不信你试试! obj.life_value-=500#假设攻击力是500 lance=Weapon("长矛",200,6,100) egg=Person("egon",10,1000,600) ha2=Dog("二愣子","哈士奇",10,1000) if egg.money > lance.price: lance.update(egg) egg.weapon=lance#egg的新属性 print(egg.__dict__) print(egg.weapon.__dict__) print(egg.money,egg.life_value,egg.aggre) print(egg.weapon.name,egg.weapon.price,egg.weapon.aggre,egg.weapon.life_value) egg.attack(ha2)#egg攻击了2哈 print(ha2.life_value) egg.weapon.prick(ha2)#等同于lance.prick(ha2) #egg用武器攻击了2哈 print(ha2.life_value)

 

 

类的命名空间VS实例/对象的命名空间

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

而类的属性分两种:静态属性和动态属性

  • 静态属性 就是直接在类中定义的变量
  • 动态属性 就是定义在类中的方法fun(self)   

创建一个对象/实例,就会创建一个对象/实例的名称空间,用来存放对象/实例的名字,这些名字被成为对象/实例的属性

在对象obj.name 会先从obj 自己的名称空间里找name,找不到则会去类中找,类中找不到就去父类中找,最后找不到就抛异常

 

面向对象的重要用法之“组合”

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

通俗的讲就是两个类之间是一种“有” 的关系。比如学校这个类里有老师类,学生类

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

class Birthday:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
class Couse:
    def __init__(self,name,price,period):
        self.name=name
        self.price=price
        self.period=period
class Teacher:
    def __init__(self,name,gender,birth,course):
        self.name=name
        self.gender=gender
        self.birth=birth
        self.course=course
    def teach(self):
        print("teaching.")
t1=Teacher('egon','male',
           Birthday("1984","6","6"),
           Couse("python",9999,"6 months"))
print(t1.birth.year,t1.birth.month,t1.birth.day)
print(t1.course.price)

 

class Birthday:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
class Couse:
    def __init__(self,name,price,period):
        self.name=name
        self.price=price
        self.period=period
class Teacher:
    def __init__(self,name,gender):
        self.name=name
        self.gender=gender
    def birth(self,birth_obj):
        print("birth day is %s-%s-%s" %(birth_obj.year,birth_obj.month,birth_obj.day))
    def couse(self,couse_obj):
        print("couse is name is %s,price is %s,period is %s" %(couse_obj.name,couse_obj.price,couse_obj.period))
    def teach(self):
        print("teaching.")

t1=Teacher('egon','male')
b1=Birthday("1984","6","6")
c1=Couse("python","9999","6 months")
t1.birth(b1)
t1.couse(c1)

面向对象的三大特性:

 1继承,2多态,3封装

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

 python中类的继承分为单继承和多继承

 

  class ParenClass1:#定义父类1
    pass
class ParenClass2:#定义父类2
    pass
class SubClass1(ParenClass1):#单继承,基类是ParenClass1,派生类是SubClass1
    pass
class SubClass2(ParenClass1,ParenClass2)#多继承,用逗号分割多个继承的类
    pass

 查看继承

__base__:只查看从左到又继承的第一个父类

__bases__:则是查看所有继承的父类

友情提示:如果没有制定基类,python的类会默认继承object类,object类是所有python类的基类,它提供了一下常见方法的体现.如(__str__)的实现

print(SubClass1.__bases__)
(<class '__main__.ParenClass1'>,) print(SubClass2.__bases__)
(<class '__main__.ParenClass1'>, <class '__main__.ParenClass2'>)

 继承与抽象   

先抽象再继承   抽象即抽取类似或者相像的部分

抽象最主要的作用是划分类别(分类)

抽象只是分析和设计的过程中,一个动作或者说是一种技巧。通过抽象可以得到一个类

 

 

继承:是基于抽象的结果,通过编程语言去实现它。

          首先是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构

 

 

 继承与重用性

在开发的过程中,如果我们定义了一个类A,然后又想建立一个类B,但是类B的大部分内容和类A的相同。这时我们不能可能从头开始再写一个类B,这就用到了类的继承 这个概念

 通过继承的方式新建类B,让B继承A,B会遗传A的所有属性(静态属性和动态属性<fun>)实现代码重用

class Animal:
    def __init__(self,name,aggre,life_value):
        self.name=name
        self.aggre=aggre
        self.life_value=life_value
    def eat(self):
        print("%s is eating" %self.name)
class Dog(Animal):
    pass
class Person(Animal):
    pass
egg=Person("egon",100,1000)
egg.eat()
ha2=Dog("旺财",50,500)
ha2.eat()

 提示:子类不重写__init__ , 实例化子类时,会自动调用父类定义的__init__

 

  但重写了__init__时,实例化子类,就不会调用父类已经定义的__init__。为了能使用或扩展父类的行为,最好显示调用父类的__init__方法

class Animal:
    def __init__(self,name,aggre,life_value):
        self.name=name
        self.aggre=aggre
        self.life_value=life_value
    def eat(self):
        print("%s is eating" %self.name)
class Dog(Animal):
    def __init__(self,name,aggre,life_value,breed):
        #Animal.__init__(self,name,aggre,life_value)  #指名道姓法
        super().__init__(name,aggre,life_value)       #super()法  推荐用   相当于super(DOG,slef).__init__(name,aggre,life_value) Dog的父类
        self.breed=breed
    def bite(self,obj):
        obj.life_value-=self.life_value
    def eat(self):
        print("from dog")
class Person(Animal):
    def __init__(self,name,aggre,life_value,money):
        Animal.__init__(self,name,aggre,life_value)
        self.money=money
    def attack(self,obj):
        obj.life_value-=self.aggre
    def eat(self):
        Animal.eat(self)
        print("from person")


egg=Person("egon",10,1000,600)
ha2=Dog("旺财","哈士奇",10,1000)
print(egg.name)
print(ha2.name)
egg.eat()

 

派生

子类在父类方法和属性的基础上产生了新的方法和属性就叫派生

需要注意的是,一旦重新定义了自己的属性并且与父类的重名,那么在调用新增的属性时,就以自己的为准

通过继承建立了派生类与基类之间的关系,它是一种“是”的关系,比如白马是马,人是动物,奥巴马是总统。

当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较号。

 

 

 抽象类与接口类

继承顺序

新式类:广度优先

经典类:深度优先

 多态

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

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

import abc
class Animal(metaclass=abc.ABCMeta):#同一类事物:动物
    @abc.abstractclassmethod
    def talk(self):
        print("base..")
class People(Animal):#动物的形态之一:人
    def talk(self):
        print("say hello")
class Dog(Animal):#动物的形态之二:狗
    def talk(self):
        print("say wangwang")
class Pig(Animal):#动物的形态之三:猪
    def talk(self):
        super().talk()
        print("say ao ao")
p1=Pig().talk()
d1=Dog().talk()

 文件有多种形态,文本文件,可执行文件

import abc
class File(metaclass=abc.ABCMeta):#同一类事物:文件
    @abc.abstractclassmethod
    def click(self):
        pass
class Text(File):#文件的形态之一:文本文件
    def click(self):
        print("open file")
class Exefile(File):#文件的形态之二:可执行文件
    def click(self):
        print("execute file")
e1=Exefile().click()

python自带多态。 当子类和父类都存在相同的 click()方法时,子类的 click() 覆盖了父类的 click(),在代码运行时,会调用子类的 click() .这样,我们就获得了继承的另一个好处:多态

 

 封装

在python 中用双下划线开头的方法(__fun)将属性隐藏起来(设置成私有的) 私有属性和私有方法在原则上是在类的内部调用的

什么时候使用私有变量

1 在继承中,父类如果不想让子类继承自己的方法,可以将方法定义为私有的

2 当我们想要隐藏一个变量不被外界调用的时候

 

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"#当子类中有相同方法时执行子类方法


class A:
    def __fa(self): #在定义时就变形为_A__fa 可通过print(A.__dict__) 查看'_A__fa': <function A.__fa at 0x025C3DF8>
        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"

 封装不是单纯意义的隐藏

1:封装数据

将数据隐藏起来不是目的。隐藏起来然后对外提供操作该数据的接口,我们可以在接口上附加对数据操作的限制,以完成对数据属性操作严格控制

class Teacher:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age
    def tell_info(self):
        print("name is %s,age is %s" %(self.__name,self.__age))
    def set_info(self,name,age):
        if not isinstance(name,str):  #用来判断对象是否是已知的类型isinstance(x, A_tuple)
            raise TypeError("姓名必须是字符串类型")  在Python中,要想引发异常,最简单的形式就是输入关键字raise,后跟要引发的异常的名称
        if not isinstance(age,int):
            raise TypeError("年龄必须是整形")
        self.__name=name
        self.__age=age
t=Teacher("egon",18)
t.tell_info()

t.set_info("huazai",32)
t.tell_info()

 2 封装方法:目的是隔离复杂度

你的身体没有一处不体现着封装的概念。你的身体把膀胱尿道等等这些尿尿的功能隐藏起来,然后为你提供一个尿尿的接口就可以了(接口就是你,,,),你总不能把膀胱挂到身体外面,上厕所的时候就跟别人炫耀:hi man,你瞅瞅我的膀胱,看看我是怎么尿的

 来一个隐藏复杂度的栗子

class Atm:
    def __cart(self):
        print("cart...")
    def __auth(self):
        print("auth....")
    def __input(self):
        print("input...")
    def __take(self):
        print("take...")
    def withdraw(self):
        self.__auth()
        self.__cart()
        self.__input()
        self.__take()
a=Atm()
a.withdraw()

 几个类中的装饰器(property,classmethod,staticmethod)

特性(property)

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

class People:
    def __init__(self,name,weigth,heigth):
        self.name=name
        self.weigth=weigth
        self.heigth=heigth
    @property
    def bmi(self):
        return self.weigth / (self.heigth**2)
p1=People('huazai',70,174)
print(p1.bmi)# 调用类的方法时就像在调用类的静态属性一样

 为什么要用property

Python内置的@property装饰器就是负责把一个方法变成属性调用的:

有没有既能检查参数,又可以用类似属性这样简单的方式来访问类的变量呢?答案就是这个装饰器@property

@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

 

class Foo:
    def __init__(self,val):
        self.__Name=val
    @property
    def name(self):
        return self.__Name
    @name.setter
    def name(self,value):##检查传入的参数,如果类型不符合str 就抛异常!
        if not isinstance(value,str):
            raise TypeError("%s must be str"%value)
        self.__Name=value
    @name.deleter
    def name(self):
        raise TypeError("can not delete")
f=Foo('egon')
print(f.name)
f.name="huazai"
print(f.name)
del f.name

 一个静态属性@proterty 本质就是实现get set delete 三种方法

class Foo:
    @property
    def aa(self):
        print("get..")
    @aa.setter
    def aa(self,value):
        self.value=value
        print("set..%s" %self.value)
    @aa.deleter
    def aa(self):
        print("delete..")
f1=Foo()
f1.aa
f1.aa=1
del f1.aa

 给个例子property

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()
print(obj.price)#获取商品价格
obj.price=1000#修改商品原价
print(obj.price)

del obj.price#删除商品原价
print(obj.__dict__)

Python中3种方式定义类方法, 常规方式, @classmethod修饰方式, @staticmethod修饰方式.

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

 

class A(object):
    bar = 1
    def func1(self):
        print('foo')
    @classmethod
    def func2(cls):
        print('func2')
        print(cls.bar)#调用类属性======A.bar
        cls().func1()  # 调用 foo 方法 =======A().fun1()
A.func2()  # 不需要实例化 省略了(括号)

 python staticmethod 返回函数的静态方法.该方法不强制要求传递参数

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