zoukankan      html  css  js  c++  java
  • 17.python面向对象

    面向对象的编程(object oriented programming),简称OOP:是一种编程的思想。OOP把对象当成一个程序的基本单元,一个对象包含了数据和操作数据的函数。面向对象的出现极大的提高了编程的效率,使其编程的重用性增高。

    模拟场景理解面向对象和面向过程:

     1 '''
     2 使用面向过程的思想解决吃饭的问题?
     3         步骤:
     4         1).思考今天吃什么?
     5         2).去买菜(货比三家)
     6         3).摘菜
     7         4).洗菜
     8         5).切菜
     9         6).炒菜
    10         7).焖饭
    11         8).吃饭
    12         9).洗刷
    13 使用面向对象的思想解决吃饭的问题?
    14         步骤:
    15         1).思考今天吃什么?
    16         2).去饭店
    17             ①.调用服务员的点菜功能
    18             ②.将菜品告知后台大厨
    19             ③.大厨调用服务员的上菜功能
    20         3).开始吃饭
    21         4).结账走人(多种支付方式)
    22 '''

    Python是解释性语言,但是它是面向对象的,能够进行对象编程。面向对象是一种编程方式,此编程方式的实现是基于对  和 对象 的使用;类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中);对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数。大白话理解类和对象的区别:i类:具有一些列相同特征、行为的"事物",它的表现是不具体的、不清晰的、模糊的概念;对象:从类中实例化得到,一个实实在在的个体,在内存中有体现;它的表现是具体的、清晰的、看得见摸的着的。

    遇到面向对象的问题,通常可以考虑如下三个环节:

      1). 设计类,定义属性、函数、...(可能需要花费大量的时间) ;

      2). 创建(实例化)对象(简单,一行代码搞定,但是内存比较复杂);

      3). 对象调用属性或者函数完成需求。

     1 # 1.设计类
     2 class Car(object): 
     3      
     4     # 属性
     5     color = "红色"
     6     brand = "BMW"
     7     number = "沪A88888"
     8 
     9     # 函数/方法
    10     def run(self):
    11         print('%s的%s,车牌为%s,正在飞速的行驶...' %(self.color,self.brand,self.number))
    12 
    13     def stop(self):
    14         print('车停了...')
    15 
    16 # 2.创建对象/实例化对象
    17 c1 = Car()
    18 print(c1,type(c1))                  # 得到<__main__.Car object at 0x000000000220D710> <class '__main__.Car'>
    19 
    20 # 3.对象调用属性
    21 print(c1.color,c1.brand,c1.number)  # 红色 BMW 沪A88888
    22 
    23 # 4.对象调用函数
    24 c1.run()                            # 红色的BMW,车牌为沪A88888,正在飞速的行驶...
    25 c1.stop()                           # 车停了...
    26 
    27 # 创建第二个对象
    28 c2 = Car()
    29 print(c2,type(c2))                  # 得到<__main__.Car object at 0x000000000272D7B8> <class '__main__.Car'>
    30 
    31 print(c1 == c2)                     # 得到False ,比较的是地址,c1和c2的地址不一样
    32 
    33 c2.color = "白色"
    34 c2.brand = "BYD"
    35 c2.number = "京A66666"
    36 print(c2.color,c2.brand,c2.number)   # 白色 BYD 京A66666
    37 print(c1.color,c1.brand,c1.number)   # 红色 BMW 沪A88888
    38 '''在一个模块中可以创建多个对象,它们彼此之间是相互独立存在(堆空间有体现),切互不干扰...'''
    39 
    40 c3 = c1                              # c1和c3的地址是一样的,共用,c1不调用了,但c3还指在堆上,c1记录的地址又给到c3一份
    41 c1 = None
    42 '''此时堆中有1个空间,不存在垃圾空间;因为c1虽然被赋值为None了,但是c3仍然记录了堆中对象空间的地址(维护这层关系)'''

    类的成员:

    1). 字段:普通字段、静态字段

    普通字段需要通过对象来访问,而静态字段通过类访问,在使用上可以看出普通字段和静态字段的归属是不同的;他们的本质的区别是内存中保存的位置不同,普通字段属于对象,而静态字段属于类;静态字段在内存中只保存一份,普通字段在每个对象中都要保存一份;通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段。

     1 class Province:
     2 
     3     # 静态字段/类属性
     4     country = '中国'
     5 
     6     def __init__(self, name):
     7         # 普通字段/对象属性
     8         self.name = name
     9 
    10 # 实例对象obj1
    11 obj1 = Province('河北省')
    12 print(obj.name)   # 直接访问普通字段
    13 
    14 # 实例对象obj2
    15 obj2 = Province('河南省')
    16 print(obj.name)   # 直接访问普通字段
    17 
    18 # 直接访问静态字段
    19 Province.country

    2). 方法:普通方法、类方法、静态方法、属性方法

    前三种方法在内存中都归属于类,区别在于调用方式不同。普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;类方法:由类调用;至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;静态方法:由类调用;无默认参数。

    相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份;不同点:方法调用者不同、调用方法时自动传入的参数不同。

    静态方法意思是把 @staticmethod 下面的函数和所属的类截断了,这个函数就不属于这个类了,没有类的属性了,只不是还是要通过类名的方式调用  ,把静态方法当作一个独立的函数给他传参就行了。

    类方法只能访问类变量,不能访问实例变量。

    属性方法把一个方法变成一个静态属性,属性就不用加小括号那样的去调用了

     1 class Foo:
     2 
     3     def __init__(self, name):
     4         self.name = name
     5 
     6     def ord_func(self):
     7         """ 定义普通方法,至少有一个self参数 """
     8         print('普通方法')
     9 
    10     @classmethod
    11     def class_func(cls):
    12         """ 定义类方法,至少有一个cls参数 """
    13         print('类方法')
    14 
    15     @staticmethod
    16     def static_func():
    17         """ 定义静态方法 ,无默认参数"""
    18         print('静态方法')
    19 
    20 # 调用普通方法
    21 f = Foo()
    22 f.ord_func()
    23 
    24 # 调用类方法
    25 Foo.class_func()
    26 
    27 # 调用静态方法
    28 Foo.static_func()

    属性方法:其实是方法里面普通方法的变种。属性方法的定义和调用要注意:定义时,在普通方法的基础上添加 @property 装饰器;定义时,属性仅有一个self参数调用时,无需括号。补充:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象,属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回。

     1 class Foo:
     2 
     3    # 定义普通方法
     4    def func(self):
     5        pass
     6 
     7    # 定义属性
     8    @property
     9    def prop(self):
    10        pass
    11 
    12 # 实例化对象
    13 foo_obj = Foo()
    14 
    15 foo_obj.func() # 调用普通方法
    16 foo_obj.prop   # 调用属性方法

    属性方法的两种定义方式:装饰器 即:在方法上应用装饰器;静态字段 即:在类中定义值为property对象的静态字段。

     1 # 装饰器方式:在类的普通方法上应用@property装饰器
     2 class Goods:
     3 
     4     @property
     5     def price(self):
     6         return "wupeiqi"
     7 
     8 obj = Goods()
     9 
    10 obj.price  # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
    11 
    12 
    13 # 静态字段方式,创建值为property对象的静态字段
    14 class Foo:
    15 
    16     def get_bar(self):
    17         return 'wupeiqi'
    18 
    19     BAR = property(get_bar)
    20 
    21 obj = Foo()
    22 
    23 obj.BAR   # 自动调用get_bar方法,并获取方法的返回值

    类特殊成员:

    魔术函数:__开头并且__结尾的函数,我们称为魔术函数;特点:调用执行都不需要程序员关注,系统自行决定;例如:__init__、__del__、__str__ 、......  构造函数,析构函数,  重写函数,...... 

    构造函数(constructor):又称构造方法/构造器,在生成对象时调用,一个对象只会被执行一次,可以用来进行一些初始化操作,不需要显示去调用,系统会默认去执行。

    格式:__init__(self):

    执行时机:在创建对象时被执行

     1 class Person(object):
     2 
     3     def __init__(self,name,age):
     4         self.name = name     
     5         self.age = age
     6         self.address = '中国'
    6 7 def details(self): 8 print('姓名为:%s,年龄为:%d,籍贯是:%s' %(self.name,self.age,self.address)) 9 10 # 创建对象 11 p1 = Person("多多",18) # 构造方法,通过类创建对象时,自动触发执行 12 # 创建第二个对象,与p1互不干扰,共同存在与堆中 13 p2 = Person("老王",28) 14 p2.details()

    析构函数:当对象在内存中被释放时,自动触发执行,此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。这个方法默认是不需要写的,不写的时候,默认是不做任何操作的。因为不知道对象是在什么时候被垃圾回收掉,所以,除非确实要在这里面做某些操作,不然不要自定义这个方法。

    格式:__del__(self):

    执行时机:在程序结束前,将对象回收,清出内存

     1 class Dog:
     2 
     3     def __init__(self,name,age,color):
     4         print('我是构造函数...')
     5         self.name = name      
     6         self.age = age
     7         self.color = color
     8 
     9     def __del__(self):
    10         print('我是析构函数...')
    11     
    12     def func(self):
    13         print('我是func函数...')

    __str__(self)函数:如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值

    对象实例化之后将数据给到对象名,此时如果打印对象名,在控制台上我们看到的是整个对象的类型以及在内存中的地址(十六进制),但是我们在开发过程中,对于类型和地址并不关注;我们更希望看到的是对象的各个属性内容,此时我们可以自己重新定义__str__(self)函数的函数体(就是函数重写),此函数有返回值,return后面的内容必须是str类型

    执行时机:在打印对象名/引用名时被触发

     1 # 没有定义__str__方法时,打印创建的对象
     2 class Person(object):
     3 
     4     def __init__(self,name,age,address):
     5         self.name = name
     6         self.age = age
     7         self.address = address
     8 
     9 p = Person('韩梅梅',20,'上海')
    10 print(p)          # <__main__.Person object at 0x0000000004C3D978>
    11 
    12 
    13 # 有定义__str__方法时,打印创建的对象
    14 class Person(object):
    15 
    16     def __init__(self,name,age,address):
    17         self.name = name
    18         self.age = age
    19         self.address = address
    20 
    21     def __str__(self):
    22         return '姓名为:%s,年龄为:%d,籍贯是:%s人' %(self.name,self.age,self.address)   # return后面必须是字符串数据
    23 
    24 p = Person('韩梅梅',20,'上海')
    25 print(p)          # 姓名为:韩梅梅,年龄为:20,籍贯是:上海人

     __dict__方法:这个方法是以字典的形式列出类或对象中的所有成员,在类里面有,在对象里面也有。

     1 class abc:
     2     def __init__(self,age):
     3         self.age=age
     4     def __add__(self,obj):
     5         return self.age+obj.age
     6 
     7 a1=abc(18)
     8 
     9 print(abc.__dict__) # 类里面的所有成员{'__add__': <function abc.__add__ at 0x0000020666C9E2F0>, '__module__': '__main__', '__weakref__': 
    10                     # <attribute '__weakref__' of 'abc' objects>, '__init__': <function abc.__init__ at 0x0000020666C9E268>, '__doc__': None, 
    11                     # '__dict__': <attribute '__dict__' of 'abc' objects>}
    12 
    13 print(a1.__dict__)  # 对象里的成员{'age': 18}

    面向对象的三大特性:封装性、继承性、多态性

    1). 封装(Encapsulation):将内容封装到某个地方,以后再去调用被封装在某处的内容。 对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。

    计算机层面:①.模块、类、函数...②.属性数据的封装与隐藏 (数据私有化);封装的好处:安全性提高了。

    场景演示:

     1 class Person:
     2     def __init__(self,name,age,money):
     3         self.name = name
     4         self.age = age
     5         self.money = money
     6     def __str__(self):
     7         return "name:%s,age:%s,money:%s"%(self.name,self.age,self.money)
     8 
     9 # 实例化Person对象
    10 p = Person('tom',30,10000)
    11 print(p)      # name:tom,age:30,money:10000
    12 
    13 # age可以设置值,但是不符逻辑了
    14 p.age=-40      
    15 print(p)      # name:tom,age:-40,money:10000

    以上情况不会出现编译和运行异常,但是出现了数据不符合逻辑的情况;关系到对象直接在外部去操作数据(属性),导致"脏数据"的出现;要解决此问题,需要将上面的年龄age私有化,一旦私有化age之后,那么age使用的方位只有在class中,出了class外界无法使用他,使用__属性名的方式。
    1).首先第一步是在外界不允许对象直接操作/访问属性(将此权利没收) --> 将属性私有化:__属性名
    2).需要在类的内部提供给外界额外的访问方式(函数:getter/setter)

     1 class Person:
     2     def __init__(self,name,age,money):
     3         self.name = name
     4         self.__age = age
     5         self.money = money
     6     def __str__(self):
     7         return "name:%s,age:%s,money:%s"%(self.name,self.__age,self.money)
     8 
     9 # 实例化Person对象
    10 p = Person('tom',30,10000)
    11 print(p)      # name:tom,age:30,money:10000
    12 
    13 p.age=-40     # 现在此行代码相当于动态为对象p添加一个属性age  
    14 print(p)      # name:tom,age:30,money:10000,年龄任然是30
    15 
    16 # 查看对象p中所有的成员变量(属性)
    17 print(p.__dict__)   # 得到{'name': 'tom', '_Person__age': 30, 'money': 10000} 
    18 '''所以age私有化之后,在计算机底层真正的名字已经变成了_Person__age,一个属性一旦被私有化,在底层真正的名字是:_类名__属性名 19 其实python的私有化我们可以理解为伪私有(只是换了个名)''' 20 21 # 以下的操作仅仅是为对象p动态添加一个属性为__age 22 p.__age=-50 23 print(p) # 仍然得到name:tom,age:30,money:10000 24 print(p.__dict__) # 再看属性{'name': 'tom', '_Person__age': 30, 'money': 10000, '__age': -50} ,只是多了一个名为__age的参数 25 26 '''但是动态数据还是可以改的(但是不要去改,这样私有化就没意义了)''' 27 p._Person__age = -100 28 print(p) # 得到name:tom,age:-100,money:10000

    私有化之后可以不会出现逻辑不符的现象,但是对于age,需要在类的内部提供给外界额外的访问方式(函数:getter/setter);

    格式:get属性名(self)-->有返回值;set属性名(self,变量参数)-->有返回值;

    以上两个函数的属性名都满足首字母大写其余字母小写的规范。

     1 class Person:
     2     def __init__(self,name,age,money):
     3         self.name = name
     4         self.__age = age
     5         self.money = money
     6 
     7     # 设置__age
     8     def setAge(self,age):
     9         # 对age值进行合法性的校验
    10         if age < 0 or age > 130:
    11             raise Exception('年龄不合法...')
    12         else:
    13             self.__age = age
    14 
    15     # 获取__age
    16     def getAge(self):
    17         return self.__age
    18 def __str__(self): 19 return "name:%s,age:%s,money:%s"%(self.name,self.__age,self.money) 20 21 p = Person('tom',30,10000) 22 print(p) # name:tom,age:30,money:10000 23 24 # 调用函数完成设置和获取属性值的操作 25 print(p.getAge()) # 30 26 p.setAge(40) 27 print(p) # name:tom,age:40,money:10000 28 29 p.setAge(-40) 30 print(p) # Exception: 年龄不合法...

    总结:python的类中只有私有成员和公有成员两种,不像c++中的类有公有成员(public),私有成员(private)和保护成员(protected).并且python中没有关键字去修饰成员,默认python中所有的成员都是公有成员,但是私有成员是以两个下划线开头的名字标示私有成员,私有成员不允许直接访问,只能通过内部方法去访问,私有成员也不允许被继承。在类的内部提供外界额外的访问方式(定义setter和getter方法),并且在需要的时候,可以在函数的内部加入数据合法性的校验;模板:对于setter函数,命名:set属性名(首字母大写);对于getter函数,命名:get属性名(首字母大写)

    2). 继承性(Inheritance):面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。计算机层面:两部分组成,一部分我们称为父类(基类、超类、superclass);另一部分我们称为子类(派生类、subclass);子类可以使用父类中的成员(使用权)。

    继承性的好处:1).代码复用性变强;2).代码扩展性变强;3).代码维护性变好;4).代码阅读性变好;继承性弊端:类和类之间是一种强耦合关系,继承的好处要远远多于弊端,所以我们还是要经常使用继承的(合理),但不能为了继承而继承。 

    继承体系可以很庞大(呈现树状结构图),越往上层的类,感觉越模糊,越不清晰越往下层的类,感觉越清晰,越具体,所以得出结论,开发过程中创建父类的可能性变低,子类实例化的可能性极高。注意事项:1).由于继承的特点,子类对象被实例化,但是可能需要为父类属性赋值,那么可以在子类的构造函数中显示的调用父类构造来实现;2) .记住:虽然父类构造被执行,但是它仅仅做的就是赋值这件事,内存中的对象只有子类对象一个。

    分类:1).单继承(单一继承);2).多重继承;3).多继承(很多语言是不合法的)

    1). 单继承的使用:

     1 # 父类
     2 class Person:
     3     def __init__(self,name,age):
     4         print('我是Person类的构造函数。。。')
     5         self.name = name
     6         self.age = age
     7     #
     8     def eat(self):
     9         print('吃一个...')
    10     #
    11     def sleep(self):
    12         print('睡一会...')
    13 
    14 # 子类
    15 class Teacher(Person):
    16     def __init__(self,name,age,salary):
    17         print('我是teacher类的构造函数。。。')
    18         self.salary = salary
    19         # 在子类构造函数中显示的调用其父类构造;调用父类构造函数的目的:父类的属性由父类自己赋值
    20         # 方法1:super(Teacher,self).__init__(name,age)
    21         # 方法2:super().__init__(name,age)
    22         # 方法3:调用父类,这种方式最好
    23         Person.__init__(self,name,age)    
    24 
    25     # 教学
    26     def teach(self):
    27         print('教书育人...')
    28 
    29 # 实例化子类对象
    30 t = Teacher('老郭',30,6000.0)
    31 # 调用属性
    32 print(t.name,t.age,t.salary)
    33 # 调用函数
    34 t.eat()
    35 t.sleep()
    36 t.teach()

    2). 多重继承的使用:

     1 # 定义生物类:
     2 class Creature:
     3     def __init__(self,age):
     4         self.age = age
     5 
     6     def breath(self):
     7         print('呼吸...')
     8 
     9 # 定义动物类:
    10 class Animal(Creature):
    11     def __init__(self,age,name):
    12         self.name = name
    13         super().__init__(age)
    14 
    15     def eat(self):
    16         print('吃饭...')
    17 
    18 # 定义狗类
    19 class Dog(Animal):
    20     def __init__(self,age,name,color):
    21         self.color = color  
    22         Animal.__init__(self,age,name)
    23 
    24     def wangwang(self):
    25         print('犬吠...')
    26 
    27     def __str__(self):
    28         return "name:%s,age:%s,color:%s" %(self.name,self.age,self.color)
    29 
    30 d = Dog(3,'旺财','black')
    31 print(d)     # 因为定义了__str__,即重写,所以可以直接看到属性,不然 print(d)得到的是打印d的地址而已,只有print(t.name,t.age,t.color),才能看到结果
    33 d.wangwang()
    34 d.eat()
    35 d.breath()

    3). 多继承的使用:大白话就是一个子类可以调用多个父类 

     1 # 定义Father类
     2 class Father:
     3     def __init__(self,money):
     4         self.money = money
     5 
     6     def drinking(self):
     7         print('喝喝喝...')
     8 
     9 # 定义Mother类
    10 class Mother:
    11     def __init__(self,faceValue):
    12         self.faceValue = faceValue
    13     def shopping(self):
    14         print('买买买...')
    15 
    16 # 定义Child类,同时继承Father和Mother类
    17 class Child(Father,Mother):
    18     def __init__(self,money,faceValue,work):
    19         self.work = work
    20         Father.__init__(self,money)
    21         Mother.__init__(self,faceValue)
    22 
    23     def playing(self):
    24         print('玩玩玩...')
    25 
    26 # 实例化子类对象
    27 child = Child(1000000,True,"语数外")
    28 
    29 # 调用函数
    30 child.playing()      # 玩玩玩...
    31 child.drinking()     # 喝喝喝...
    32 child.shopping()     # 买买买...

    函数重写(复写,覆盖,override) :

    前提:必须有继承性;原因:父类中的功能(函数),子类需要用,但是父类中函数的函数体内容和我现在要执行的逻辑还不相符,那么可以将函数名保留(功能还是此功能),但是将函数体重构;注意:子类重写父类的函数,除了函数体以外的部分,直接复制父类的即可

     1 class Fu:
     2     def func(self):
     3         print('辟邪剑法...')
     4 
     5 class Zi(Fu):
     6     def func(self):
     7         super().func()
     8         print('葵花宝典...')
     9 
    10 # 实例化子类对象
    11 zi = Zi()    
    12 
    13 zi.func()     # 得到 辟邪剑法
    14               #     葵花宝典

    ......

  • 相关阅读:
    Java ArrayList,LinkedList使用
    Vue 使用axios分片上传
    Vue 中Axios 使用
    Vue 自定义组件
    Java IO系统--RandomAccessFile
    Java IO系统--字符流
    Java String类
    静态导入(static import)
    枚举(Enum)
    Java 可变参数
  • 原文地址:https://www.cnblogs.com/bonheur/p/12374407.html
Copyright © 2011-2022 走看看