飞道的博客

python:类基础

395人阅读  评论(0)

什么是面向对象编程

1、面向对象编程(oop)是一种程序设计思想。oop把对象作为程序的基本单元,一个对象包含数据和操作数据的函数

2、在python中,所有数据类型都被视为对象,也可以自定义对象。自定义对象数据类型就是面向对象中类的概念

 

 

面向对象术语简介

1、类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例

2、方法:类中定义的函数

3、类变量(属性):类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体(方法)之外。类变量通常不作为实例变量使用,类变量也称作属性

4、数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据

5、方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写

6、实例变量:定义在__init__方法中的变量,只作用于当前实例的类

7、继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待,以普通的类为基础建立专门的类对象

8、实例化:创建一个类的实例,类的具体对象。一个类可以实例化出无数个对象

9、对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法

10、多态:对不同类的对象使用同样的操作

11、封装:对外部世界隐藏对象的工作细节

 

 

1、类提供了一种组合数据和功能的方法。创建一个新类意味着创建一个新的对象类型,从而允许创建一个该类型的新实例

2、每个类的实例可以拥有保存自己状态的属性。一个类的实例也可以有改变自己状态的(定义在类中的)方法

3、Python的类提供了面向对象编程的所有标准特性:
    ⑴类继承机制允许多个基类,派生类可以覆盖它基类的任何方法,一个方法可以调用基类中相同名称的的方法。
    ⑵对象可以包含任意数量和类型的数据
    ⑶和模块一样,类也拥有Python天然的动态特性:它们在运行时创建,可以在创建后修改

 

 

类的定义

python中类的定义语法如下:


  
  1. class ClassName:
  2.     <statement -1>
  3.     .
  4.     .
  5.     .
  6.     <statement-N>
  7.     

注:
1、python中定义类使用class关键字,class后面紧接类名,类名通常是大写开头的单词

2、类包含属性和方法
    ⑴属性:分为类属性和实例属性
    ⑵方法:即定义在类中的函数(与普通的函数类似)

3、一个对象的特征称为"属性",一个对象的行为称为"方法"。属性在代码层面上来看就是变量,方法实际就是函数,通过调用这些函数来完成某些工作

4、在类中定义方法的形式和函数差不多,但其不称为函数,而是叫方法。方法的调用需要绑定到特定的对象上(通过self.或实例对象名),而函数不需要

 

 

类的使用

例1:


  
  1. class MyClass:
  2.     """一个简单的类实例"""
  3.     i = 12345 #定义一个类属性
  4.     def f(self): #定义一个类方法
  5.         return 'hello world'
  6. x = MyClass()   # 实例化类
  7. # 访问类的属性和方法
  8. print( "MyClass 类的属性 i 为:", x.i)
  9. print( "MyClass 类的属性 i 为:", MyClass.i)
  10. print( "MyClass 类的方法 f 输出为:", x.f())
  11. """
  12. MyClass 类的属性 i 为: 12345
  13. MyClass 类的属性 i 为: 12345
  14. MyClass 类的方法 f 输出为: hello world
  15. """

注:
1、在未实例化类时(x = MyClass()前),只是定义了对象的属性和方法,此时其还不是一个完整的对象,将定义的这些称为类(抽象类)。需要使用类来创建一个真正的对象,这个对象就叫做这个类的一个实例,也叫作实例对象(一个类可以有无数个实例)

2、创建一个对象也叫做类的实例化,即x = MyClass()。(此时得到的x变量称为类的具体对象)。注意此时类名后面是跟着小括号的,这跟调用函数一样。另外赋值操作并不是必须的,但如果没有将创建好的实例对象赋值给一个变量,那这个对象就没办法使用,因为没有任何引用指向这个实例。一个类可以实例化出无数个对象

3、如果要调用对象里的方法,就需要判断是在类中调用还是在类外调用:
    ⑴在类中调用类方法:self.方法名(参数)或类名.方法名(self,参数)这个这里只是提一下,可以先不纠结
    ⑵在类外调用类方法:实例对象名.方法名(参数)。这里的例子就是类外调用方法,只是说类方法中没有定义参数。x.f():x为实例对象名,f()为类中定义的方法
    
4、 x.i和MyClass.i都是用于调用类的属性,也就是我们前面所说的类变量;x.f()用于调用类的方法
    ⑴调用类属性可以使用:实例对象名.类属性名或类名.类属性名。虽然这两种方法都可以调用类属性,但是两者在调用使用还是有区别的,后面介绍

5、类中定义方法的要求:在类中定义方法时,第一个参数必须是self,除第一个参数外,类的方法和普通的函数没什么区别,如可以使用默认参数,可变参数,关键字参数和命名关键字参数等
    ⑴虽然在定义方法时会定义一个self参数,但是不管是在类中或是在类外调用方法都不用传递self,其他参数正常传入
    ⑵self参数究竟是什么,这里也可以先不纠结,目前只需要知道定义一个类方法时,第一个参数必须是self,但是在调用类方法时,不需要传递这个self参数

6、类对象(抽象类)支持两种操作:即属性引用和实例化
    ⑴属性引用:方法为类名.类属性名(也可以实例对象名.类属性名)
    ⑵实例化:将一个抽象类实例化成一个实例对象(x = MyClass() )。一个类可以实例化出无数个对象

7、类是一个抽象的概念,对象则是一个实际存在的东西。就像我们说的"狮子",它只是一个抽象的东西,只有具体到狮子这种动物身上它才是实际存在的。在比如设计房子的图纸只能告诉你房子是什么样的,并不是真正的房子,只有通过钢筋水泥建造出来的房子才实际存在,才能住人。
    ⑴"造房子"这个过程就相当于是"实例化类',一个抽象类只有实例化成一个具体的实例对象后,才会有意义

 

 

self是什么:为什么方法中第一个参数必须为self

1、self其实就相当于C++中的this指针

2、如果把类比作图纸,那么由类实例化后的对象才是真正可以住人的房子。根据一张图纸就可以设计出成千上万的房子,他们都长得差不多,但他们都有不同的主人,每个人都只能回自己的家里.....所以self这里就相当于每个房子的门牌号,有了self就可以轻松找到自己房子

3、python中的self参数就是同一个道理,由同一个类可以生成无数对象,当一个对象的方法被调用的时候,对象会将自身的引用作为第一个参数传给该方法,那么python就知道需要操作哪个对象的方法了
4、简单的来说就是:self代表的当前的实例对象本身,这样在调用类方法等时Python就知道当前是哪个实例对象了
    ⑴一个抽象类实例化后,实力对象为a,那么此时self就代表实例对象a
    ⑵一个抽象类实例化后,实力对象为b,那么此时self就代表实例对象b
    
例2:


  
  1. class Ball:
  2.     def setname(self,name,age):
  3.         self.name = name #这步感觉是将一个方法中的变量变成了一个实例变量
  4.         print(age)
  5.     def kick(self):
  6.         return "我叫%s" % self.name
  7. a = Ball()
  8. b = Ball()
  9. c = Ball()
  10. a.setname( "A", 1)
  11. b.setname( "B", 2)
  12. c.setname( "C", 3)
  13. print(a.kick())
  14. print(b.kick())
  15. print(c.kick())
  16. """
  17. 1
  18. 2
  19. 3
  20. 我叫A
  21. 我叫B
  22. 我叫C
  23. """

注:从上面例子可以看出

1、有Ball类生成了三个实例对象a,b,c,这三个对象在调用kick()方法时,是通过self参数去确定究竟当前是哪个对象在调用方法的。因此在写类方法时一定要写self参数且其位置在第一个,在调用时就不需要传入self参数了

2、在方法中定义的参数,一般来说只能在当前方法中使用(作用域)
    ⑴如果想要一个方法中的参数能在其他方法中使用,那么就可以使用"self."来将这个参数变成一个实例变量(实例变量后面介绍,这里主要是遇到了这种写法)
    ⑵name参数:在方法中使用了"self.name = name",这步就相当于是将这个name参数变成了一个实例变量,因此可以在所有方法中使用(感觉这种写法比较少,因为一个实例变量最好直接定义在__init__方法中)
    ⑶age参数:age参数就没有使用name参数那样的写法,仅仅是在setname()方法中定义并使用,因此age参数就只能在setname()方法中使用,而不能在kick()方法中使用,即使他们是在同一个类中(经常遇到的是这种写法)

 

 

类变量和实例变量

1、类变量:是该类所有实例共享的属性和方法(也可以叫"类属性")。类变量是直接定义在类中的,比如例1中的"i = 12345",变量i就是一个类属性,类中的所有方法、实例都可以使用它

2、实例变量:每个实例都独有的数据(也可以叫"实例属性")。实例变量通常是定义在__init__方法中的

3、即某个属性对于每个实例都是独有的,就需要将其定义为实例变量;某个属性是每个实例同共有的就可以定义为类属性

例3:


  
  1. class Student():
  2.     address = "china"     #定义类变量address
  3.     def __init__(self,name,age):     #定义实例变量age和name
  4.         self.name = name
  5.         self.age = age
  6.     def Info(self,score): #定义在方法中的变量(普通的变量:作用域为这个方法内)
  7.         return "学生来自于%s,名字为%s,年龄为%s,成绩为%s"%(Student.address,self.name,self.age,score)
  8.         #类中访问实例变量:self.实例变量名
  9.         #类中访问类变量:类名.类变量名
  10.         #类中访问方法中的普通变量:直接变量名(且该变量只能在这个方法中使用,不能再其他方法或类外调用)
  11. student = Student( "张三", 18)   #实例化类
  12. print(student.name) #类外访问实例变量:实例名.实例属性名
  13. print(Student.address)   #类外访问类变量:类名.类属性名(也可以实例名.类属性名)
  14. print(student.Info( 98)) #类外访问类方法:实例名.方法名(参数)
  15. #另一个实例对象
  16. student_1 = Student( "李四", 20)
  17. print(student_1.name)
  18. print(student_1.address)
  19. print(student_1.Info( 100))
  20. """
  21. 张三
  22. china
  23. 学生来自于china,名字为张三,年龄为18,成绩为98
  24. 李四
  25. china
  26. 学生来自于china,名字为李四,年龄为20,成绩为100
  27. """

注:
1、在Student类中,类属性address为所有实例所共享;实例属性name和age每个student的实例独有(每个实例有不同的name和age)
    ⑴类属性:实例对象student和student_1拥有一样的address属性
    ⑵实例属性:实例对象student和student_1拥有不一样的name和age属性(每个实例独有的属性)
    
2、调用类属性:
    ⑴类中访问类变量:类名. 类属性名
    ⑵类外访问类变量:类名.类属性名或实例名.类属性名

3、调用实例属性:
    ⑴类中访问实例变量:self.实例变量名
    ⑵类外访问实例变量:实例名.实例属性名

4、调用类方法:
    ⑴类中访问类方法:self.方法名(参数)或类名.方法名(self,参数),这个例子中未涉及到
    ⑵类外访问类方法:实例名.方法名(参数)
    
5、类变量直接定义在类中,实例变量需要定义在__init__方法中
    ⑴定义类变量:类变量直接写就好了,定义一个变量然后赋值,相当于定义一个普通的变量
    ⑵定义实例变量:需要使用self来将变量绑定到实例上。self.name = name,表示将外部传来的参数name的值赋值给Student当前实例对象自己的实例变量name
    ⑶因为实例属性是每个实例独有的,而self表示实例对象本身,因此就需要将变量用self绑定到每个实例
    
6、类中存在__init__方法时,在实例化类时需要传入参数(实例变量的实参),在调用类的方法时也需要传入方法的实参(方法中存在形参时)

 

 

类对象和实例对象

类对象

Python中一切皆对象;类定义完成后,会在当前作用域中定义一个以类名为名字,指向类对象的名字

例4:


  
  1. class Dog:
  2.     pass

注:

1、上面例子中创建了一个Dog抽象类,定义好类后会在当前作用域定义名字Dog,指向类对象Dog

2、总的来说,类对象仅支持两个操作
    实例化:使用instance_name = class_name()的方式实例化,实例化操作创建该类的实例
    属性引用:使用class_name.attr_name的方式引用类属性。(类名.属性名)

 

实例对象

1、实例对象是类对象实例化的产物,实例对象仅支持一个操作:属性引用

2、属性引用:与类对象属性引用的方式类似,使用instance_name.attr_name的方式。(实例对象名.属性名)

 

 

属性绑定

1、在定义类时,通常我们说的定义属性,其实是分为两个方面的:类属性绑定、实例属性绑定(也就是定义类属性或实例属性)

2、用绑定这个词更加确切;不管是类对象还是实例对象,属性都是依托对象而存在的。我们说的属性绑定,首先需要一个可变对象,才能执行绑定操作,使用objname.attr = attr_value的方式,为对象objname绑定属性attr。这分两种情况:
    ⑴若属性attr已经存在,绑定操作会将属性名指向新的对象
    ⑵若不存在,则为该对象添加新的属性,后面就可以引用新增属性


类属性绑定

Python作为动态语言,类对象和实例对象都可以在运行时绑定任意属性。因此,类属性的绑定发生在两个地方
    ⑴类定义时
    ⑵运行时任意阶段
例5:


  
  1. class Dog:
  2.     kind = 'canine'
  3. Dog.country = 'China'
  4. print(Dog.kind, ' - ', Dog.country)   # 输出: canine  -  China
  5. del Dog.kind
  6. print(Dog.kind, ' - ', Dog.country)   #由于上一行删除的kind属性,因此输出为AttributeError: type object 'Dog' has no attribute 'kind'

注:
1、在类定义中,类属性的绑定并没有使用objname.attr = attr_value的方式,这是一个特例,其实是等同于后面使用类名绑定属性的方式

2、因为是动态语言,所以可以在运行时增加属性,删除属性

 

实例属性绑定

与类属性绑定相同,实例属性绑定也发生在两个地方:类定义时、运行时任意阶段
例6:


  
  1. class Dog:
  2.     def __init__(self, name, age):
  3.         self.name = name
  4.         self.age = age
  5. dog = Dog( 'Lily', 3)
  6. dog.fur_color = 'red' #为实例对象dog增加一个fur_color属性
  7. print( '%s is %s years old, it has %s fur' % (dog.name, dog.age, dog.fur_color))
  8. #上面代码的输出结果为:Lily is 3 years old, it has red fur

注:
1、语句self.name = name,self.age = age以及后面的语句dog.fur_color = 'red'为实例dog增加三个属性name, age, fur_color。

2、Python类实例有两个特殊之处:
    ⑴__init__在实例化时执行
    ⑵Python实例对象调用方法时,会将实例对象作为第一个参数传递因此,__init__方法中的self就是实例对象本身,这里是dog

 

 

属性引用

属性的引用与直接访问名字不同,不涉及到作用域

 

类属性引用

类属性的引用,肯定是需要类对象的,属性分为两种:数据属性、函数属性。只是通常很少有引用类函数属性的需求

例7:数据属性引用很简单


  
  1. class Dog:
  2.     kind = 'canine'
  3. Dog.country = 'China'
  4. print(Dog.kind, ' - ', Dog.country)   # output: canine  -  China


例7_1:


  
  1. class Dog:
  2.     kind = 'canine'
  3.     def tell_kind(self):
  4.         print(Dog.kind)
  5.     def info(self):
  6.         return self.tell_kind() #类中调用类方法
  7. dog = Dog()
  8. dog.tell_kind()   # Output: canine
  9. dog.info()   # Output: canine

 

实例属性引用

使用实例对象引用属性稍微复杂一些,因为实例对象可引用类属性以及实例属性。但是实例对象引用属性时遵循以下规则:
    ⑴总是先到实例对象中查找属性,再到类属性中查找属性
    ⑵属性绑定语句总是为实例对象创建新属性,属性存在时,更新属性指向的对象

例8:


  
  1. class Dog:
  2.     kind = 'canine'
  3.     country = 'China'
  4.     def __init__(self, name, age, country):
  5.         self.name = name
  6.         self.age = age
  7.         self.country = country
  8. dog = Dog( 'Lily', 3, 'Britain')
  9. print(dog.name, dog.age, dog.kind, dog.country)   #output:Lily 3 canine Britain

注:
类对象Dog与实例对象dog均有属性country,按照规则,dog.country会引用到实例对象的属性;但实例对象dog没有属性kind,按照规则会引用类对象的属性。
 

例9:


  
  1. class Dog:
  2.     kind = 'canine'
  3.     country = 'China'
  4.     def __init__(self, name, age, country):
  5.         self.name = name
  6.         self.age = age
  7.         self.country = country
  8. dog = Dog( 'Lily', 3, 'Britain')
  9. print(dog.name, dog.age, dog.kind, dog.country)   # Lily 3 canine Britain
  10. print(dog.__dict__)                               # {'name': 'Lily', 'age': 3, 'country': 'Britain'}
  11. dog.kind = 'feline'
  12. print(dog.name, dog.age, dog.kind, dog.country)   # Lily 3 feline Britain
  13. print(dog.__dict__)                               # {'name': 'Lily', 'age': 3, 'country': 'Britain', 'kind': 'feline'}
  14. print(Dog.kind)                                   # canine (没有改变类属性的指向)

注:
使用属性绑定语句dog.kind = 'feline',按照规则,为实例对象dog增加了属性kind,后面使用dog.kind引用到实例对象的属性。这里不要以为会改变类属性Dog.kind的指向,实则是为实例对象新增属性,可以使用查看__dict__的方式证明这一点。

 

 

可变类属性引用

例10:


  
  1. class Dog:
  2.     
  3.     tricks = []
  4.     def __init__(self, name):
  5.         self.name = name
  6.     def add_trick(self, trick):
  7.         self.tricks.append(trick)
  8. d = Dog( 'Fido')
  9. e = Dog( 'Buddy')
  10. d.add_trick( 'roll over')
  11. e.add_trick( 'play dead')
  12. print(d.tricks)             output: # ['roll over', 'play dead']

注:

语句self.tricks.append(trick)并不是属性绑定语句,因此还是在类属性上修改可变对象

 

 

类、类对象、实例对象

例11:


  
  1. class C:
  2.     count = 0
  3. a = C()
  4. b = C()
  5. c = C()
  6. print(a.count,b.count,c.count)   #output:0,0,0
  7. a.count += 10   #实例对象调用类属性
  8. print(a.count,b.count,c.count)   #output:10,0,0
  9. C.count += 100   #类对象调用类属性
  10. print(a.count,b.count,c.count)   #output:10 100 100
  11. #print(count)   #name 'count' is not defined,不能直接访问类属性,具体访问方法参考前面的属性访问

注:

1、对实例对象的count属性进行赋值后,就相当于覆盖了类对象C的count属性,如果没有赋值覆盖,那么引用的就是类对象的count属性

2、即找属性的顺序为:现在当前实例中找,有就用当前实例中的,如果没有就找类中的

3、如果属性的名字跟方法相同,属性会覆盖方法

4、类变量和实例变量的区别在于:类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象

 

 

拓展:python中的self参数

例12:定义任意一个类:


  
  1. class Student(object):
  2.     pass
  3. student = Student()

注:
由于类起到模板的作用,因此,可以在创建实例的时候,把我们认为必须绑定的属性强制填写进去(绑定实例变量)。这里就用到Python当中的一个内置方法__init__方法,例如在Student类时,把name、score等属性绑上去

 

例12_1:


  
  1. class Student(object):
  2.     def __init__(self, name, score):
  3.         self.name = name
  4.         self.score = score
  5. >>>student = Student( "Hugh", 99)
  6. >>>student.name
  7. "Hugh"
  8. >>>student.score
  9. 99

注:        

1、__init__方法的第一参数永远是self,表示创建的类实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身

2、有了__init__方法,在创建实例的时候(类实例化),就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器会自己把实例变量传进去

3、这里self就是指类实例本身,self.name就是当前student实例对象的name属性,是该student实例对象独有的

4、name是外部传来的参数,不是Student类自带的。故,self.name = name的意思就是把外部传来的参数name的值赋值给student实例对象自己的实例属性name(只是实例变量的名字和形参的一样而已)
 

例12_2:


  
  1. class Student(object):
  2.     def __init__(self, name_1, score_1):
  3.         self.name = name_1
  4.         self.score = score_1
  5. student = Student( "zh", 12)
  6. print(student.name)
  7. print(student.score)
  8. """
  9. zh
  10. 12
  11. """

注:

这个例子中就特意把实例变量名和形参名定义成不一样了:self.name = name_1
    ⑴name_1相当于是占位的形参,只是方便外部向类中传递参数(给实例变量传递值),没有什么实际意义
    ⑵self.name中的name才是真正的实例变量了,是每个实例独有的属性
    ⑶只是说这两个变量表示的属性意义是一样的,因此更多的是将两个写成一样的名字。不然,有一个实例属性是"名字",形参用的是"a",实例属性用的是"name",这样就看起来比较奇怪的,都表示的是"名字"却用了不一样的名字
 

例12_3:


  
  1. class Person:
  2.     def __init__(self, name, job=None, pay=10):
  3.         self.name = name
  4.         self.job = job
  5.         self.pay = pay
  6.     def getRaise(self, percent):
  7.         self.pay = int(self.pay * ( 1 + percent))
  8.         return self.pay
  9. p = Person( "xiaoming", "jixie")
  10. print(p.getRaise( 0.8))   # output:18

注:
self是指当前被调用的对象,对的,就是上边刚被你实例化的对象p。当你需要调用当前对象(实例对象)的方法或者属性时,要用self.来进行调用(类中调用实例属性或方法:self.)
 

例13:


  
  1. class CC:
  2. def setXY(self,x,y):
  3.         self.x = x
  4.         self.y = y
  5.     def printXY(self):
  6.         print(self.x ,self.y)
  7. dd = CC()
  8. dd.setXY( 4, 5)

注:
self参数:当实例对象dd去调用setXY方法的时候,它传入的第一个参数就是dd,那么self.x = 4,self.y = 5也就相当于dd.x = 4,dd.y = 5,所以你在实例对象,甚至类对象中都看不到x和y,因为这两个属性是只属于实例对象dd的
 

例14:


  
  1. class TestClass(object):
  2.     val1 = 100
  3.     def __init__(self):
  4.         self.val2 = 200
  5.     def fcn(self, val=400):
  6.         val3 = 300
  7.         
  8.         self.val4 = val
  9.         self.val5 = 500
  10. if __name__ == '__main__':
  11.     inst = TestClass()
  12.     print(TestClass.val1)
  13.     print(inst.val1)
  14.     print(inst.val2)
  15.     #print(inst.val3)
  16.     #val3为局部变量,无法在函数为调用'TestClass' object has no attribute 'val3'
  17.     #print(inst.val4)
  18.     #print(inst.val5)

注:
1、val1是类变量,可以由类名直接调用,也可以有对象来调用;

2、val2是实例变量,可以由类的对象来调用,这里可以看出成员变量一定是以self.的形式给出的,因为self的含义就是代表实例对象

3、val3既不是类变量也不是实例变量,它只是函数fcn内部的局部变量

4、val4和val5也都不是实例变量,虽是以self.给出,但并没有在构造函数中初始化

 

 

在类方法中也可以调用其他方法

例15:


  
  1. class Dog():
  2.     def __init__(self,name,age):
  3.         self.name = name
  4.         self.age = age
  5.     def get_dog_information(self):
  6.         dog_information = "name is {0},age is {1}".format(self.name,self.age)
  7.         return dog_information
  8.     def get_dog_speak(self,love):
  9.         dog_speak = self.get_dog_information() + love
  10.         return dog_speak
  11. dog = Dog( "jake", 13)
  12. print(dog.get_dog_speak( "swimming"))
  13. #name is jake,age is 13swimming

注:在上面例子中
在get_dog_information()方法中调用了get_dog_information(),且get_dog_information()方法前加了self参数,该参数的意义与其他self的意思一样,都是代表实际本身

 

 


转载:https://blog.csdn.net/qq_39314932/article/details/80716295
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场