Python笔记6 面向对象

类的定义

面向对象两个核心:类、对象

在python中使用class定义类:

class Student():    #类名最好定义为大写开头
    name = ''       #数据成员
    age = 0
    def print_file(self):        #方法
        print('name:' + self.name)      #通过self关键字来使用变量   
        print('age:' + str(self.age))

class StudentHomework():
    homework_name = ''

student = Student()     #实例化类为对象
student.print_file()    #调用类下面的方法

类的最基本作用就是在封装代码。

类的内部不能调用类的方法,类只负责去描述和定义,而不负责执行,运行和调用类需要放在类的外部。

不推荐在一个模块(文件)中既定义类又使用类。

浅谈函数与方法的区别

在一个模块中调用另一个模块的类:

#nine.c2.py
from c1 import Student

Student = Student()
Student.print_file()

方法和函数的区别:

  1. 方法是设计层面上的称谓,更多的是面向对象中的概念;
  2. 函数是程序运行的,面向过程的一种称谓。
  3. 没有必要特别的去区分方法和函数。

变量出现在类中更多称为数据成员。

类与对象

类和对象是通过实例化关联在一起的

类是现实世界或思维世界中的实体在计算机中的反映,它将数据以及这些数据的操作封装在一起

数据成员刻画对象的特征,方法刻画对象的行为。

行为要属于主体,因此将上面的类修改为:

class Student():    #类名最好定义为大写开头
    name = ''   
    age = 0
    def do_homework(self):    #print_file不是学生主体的行为
        print('homework')

类被实例化了之后变成具体的对象,若不具体的话就不是对象。

类相当于模板,通过类产生不同的对象。

构造函数 

实例化Student 类为三个对象,三个对象是不同的

student1 = Student()
student2 = Student()
student3 = Student()
print(id(student1))
print(id(student2))
print(id(student3))

#2581261097560
#2581261097616
#2581261097672

__init__ 称为构造函数,在实例化对象时自动进行,我们也可以主动调用构造函数(实际代码中很少用到),如果显示的调用构造函数,返回的是None,并且Python规定,构造函数只能返回None,如果自己强制返回别的,会报错

如下只会返回空?

class Student():    #类名最好定义为大写开头
    name = ''       #类变量
    age = 0
    def __init__(self, name, age):
        #构造函数
        #初始化对象的属性
        name = name         #第一个name代表数据成员,第二个代表构造函数传入的形参
        age = age

student1 = Student('Julia', 18)
print(student1.name)

#

类变量与实例变量导致如上问题,详细看下面

补充原因:在尝试访问实例变量时,在实例变量列表里查找,如果没有会到类变量里面寻找。如果类中没有会到父类寻找。

区别模块变量与类中的变量

局部变量不会覆盖全局变量

c = 50
def add(x,y):
    c = x + y    #这个c不会覆盖全局变量的c
    print(c)

不能把模块的机制与类的机制混淆

类变量与实例变量 

类变量与实例变量区别:

  1. 类变量:与类关联,有类模板 Student创建
  2. 实例变量:与对象关联

对于上面返回空的问题,使用self可以解决:

class Student(): 
    sum = 0         #类变量,表示一个班级所有学生的总数   
    name = ''       #这是类变量,不可以保存对象的特征值 
    age = 0
    def __init__(self, name, age):
        self.name = name               #在Python中使用self.name 来保存对象的特征值   
        self.age = age

student1 = Student('Julia', 18)         #传递的对象特征值必须保存在对象的内部
student2 = Student('Victoria', 20)
print(student1.name)
print(student1.age)
print(student2.name)
print(student2.age)

# Julia
# 18
# Victoria
# 20

其中self不能称作关键字,但 self 可以定义为任意你想定义的标识

为什么Python中要有类变量和实例变量?类Student是一个抽象的学生,如上定义了 sum ,就是类变量,是对应于整个类的;而不同的学生对象有不同的属性,这时候就是 实例变量的作用了

类与对象的变量查找顺序

__dict__ 打印一个保存当前对象内所有变量的字典

class Student():
    name = '123' #定义变量
    age = 0
    def __init__(self,name,age):
        name = name #并没有赋值给student1
        age = age

student1 = Student('tai',18)
print(student1.name,student1.age)
print(student1.__dict__)    
#123 0
#{}

此时打印的是空字典,说明实例变量赋值没有成功

加上self,变为给实例变量赋值时,显示如下:

class Student():
    name = 'ss'
    age = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age

student1 = Student('julia', 18)
print(student1.__dict__)
print(Student.__dict__)        #打印类中变量

# {'name': 'julia', 'age': 18}
# {'__module__': '__main__', 'name': 'ss', 'age': 0, '__init__': <function Student.__init__ at 0x000001B9CBBDA9D8>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}

self与实例方法

实例方法注意:

  1. 第一个传入的参数为 self
  2. 如果在类中定义实例方法的话,要固定在类中放上self;在调用方法时,不用传入 self
  3. self 可以使用任意值,但是Python建议使用self
  4. 实例方法是实例可以调用的方法,和对象实例相关联。 

其中 self 表示:当前调用某一个方法的对象;self 并不属于Python中的一个关键字,因为换成其他任意值均可

在实例方法中访问实例变量与类变量

到目前为止,我们学习的内容思维导图

变量:表示特征

方法:表示行为,操作变量

在方法中访问变量:

class Student():
    name = 'ss'
    age = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        print(name)    #读取形参name
        print(self.name)    #读取对象的实例变量,最好使用这种方式

student1 = Student('julia', 18)

#julia
#julia

在实例方法中访问类变量:

class Student():
    name = 'ss'
    age = 0
    sum1 = 0

    def __init__(self, name1, age):
        self.name = name1
        self.age = age
        print(Student.sum1)        #第一种访问类变量的方式
        print(self.__class__.sum1)    #第二种访问类变量的方式
            
student1 = Student('julia', 18)

类方法

类变量的使用场景:

class Student():
    sum1 = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.__class__.sum1 += 1
        print('当前学生总数为: ' + str(self.__class__.sum1))
                      
student1 = Student('julia', 18)
student2 = Student('julia', 18)
student3 = Student('julia', 18)

# 当前学生总数为: 1
# 当前学生总数为: 2
# 当前学生总数为: 3

也可以除了构造函数,在其他实例方法找中调用类变量

使用类方法操作类变量:

  1. 其中第一个参数 cls 也可以使用其他变量名,cls 表示:当前调用的类
  2. 法之前加上装饰器@classmethod 决定了该方法是类方法
  3. 实例方法:与对象本身关联;类方法:与类关联
  4. 也可以使用对象来调用类方法,但是建议不要如此,因为逻辑上不通
class Student():
    sum1 = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        #self.__class__.sum1 += 1
        #print('当前学生总数为: ' + str(self.__class__.sum1))

    @classmethod        #装饰器,方法之前加上这个装饰器决定了该方法是类方法
    def plus_sum(cls):
        cls.sum1 += 1
        print(cls.sum1)
                      
student1 = Student('julia', 18)
Student.plus_sum()
student2 = Student('julia', 18)
Student.plus_sum()
student3 = Student('julia', 18)
Student.plus_sum()

# 1
# 2
# 3

静态方法

静态方法特征:

  1. 添加装饰器 @staticmethod
  2. 没有默认必须传递的参数,比如 self 或者 cls
  3. 类和对象均可以调用静态方法
  4. 类方法、静态方法均不可访问实例变量
class Student():
    sum1 = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        #self.__class__.sum1 += 1
        #print('当前学生总数为: ' + str(self.__class__.sum1))

    @classmethod        #装饰器,方法之前加上这个装饰器决定了该方法是类方法
    def plus_sum(cls):
        cls.sum1 += 1
        print(cls.sum1)

    @staticmethod
    def add(x, y):
        print('This is a static method')
                      
student1 = Student('julia', 18)
student1.add(1,2)
Student.add(3,4)

#This is a static method
#This is a static method

能用静态方法的时候都可用类方法替代,最好不要经常使用,与类和对象的关联性非常弱,和普通的函数几乎没有区别。

类中的所有变量、类到此结束了

成员可见性:公开和私有

方法、变量的内部与外部调用:

class Student():
    sum1 = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        #self.__class__.sum1 += 1
        #print('当前学生总数为: ' + str(self.__class__.sum1))

    def do_homework(self):
        self.do_english_homework()      #方法的内部调用
        print('homework')

    def do_english_homework(self):
        print('english_homework')
                      
student1 = Student('julia', 18)
student1.do_homework()      #方法的外部调用

#english_homework
#homework

当前情况下的数据成员、方法等都可以实现对象student1 的外部调用,这样一来类不安全;因为存在有些变量不可以被外部对象随意更改

Python中变量更改的规范:如果需要修改变量的值,定义方法来完成,这样可以在方法中对于参数做判断,最好不要用student1.score = -1 来赋值,如下例子中为对于 score 的修改:

class Student():
    sum1 = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.score = 0

    def marking(self, score):
        if score < 0:
            return '分数不可为负'
        self.score = score
        print(self.name + '同学的考试成绩为:' + str(self.score))
                      
student1 = Student('julia', 18)
result = student1.marking(-1)
print(result)
student2 = Student('julia', 18)
student2.marking(60)

#分数不可为负
#julia同学的考试成绩为:60

目前score是公开的public——类的外部可以直接访问。

私有的private——不可以直接访问。

不加双下划线:公开

加双下划线:私有

前后都有双下划线:公开,是python内置函数的命名风格。

没有什么是不能访问

尝试从外部访问私有变量:(基于Python动态语言的特性)

class Student():
    sum1 = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.__score = 0
                    
student1 = Student('julia', 18)
student1.__score = -1       #没有报错
#是因为这样操作会给student1对象添加一个新的属性,这并不是我们之前定义的实例变量中的__score
print(student1.__score)

student2 = Student('julia', 18) 
print(student2.__score)     #报错

# -1
# Traceback (most recent call last):
#   File "c1.py", line 76, in <module>
#     print(student2.__score)
# AttributeError: 'Student' object has no attribute '__score'

我们也可以用 __dict__ 来证明这一点:

其中'_Student__score' 表示我们定义的实例变量,它是私有的,Python在dict 中展示为'_Student__score';这也就是为什么我们在对私有变量更改时,会新添加一个属性,因为Python类内部已经把私有变量的名字改了

'__score': -1 就是我们student1.__score = -1 赋值(动态添加)之后的结果

class Student():
    sum1 = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.__score = 0
                      
student1 = Student('julia', 18)
student1.__score = -1       #没有报错, 是因为这样操作会给student1对象添加一个新的属性,这并不是我们之前定义的实例变量中的__score
print(student1.__dict__)

#{'name': 'julia', 'age': 18, '_Student__score': 0, '__score': -1}

由此可以向下延申:单下划线 + 类名 + 双下划线 + 实例变量名

通过这种方式可以间接的读取私有变量:

student2 = Student('julia', 18)
print(student2._Student__score)

因此,Python私有变量的访问全靠自觉

继承

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

最难讲清楚的反而是封装性,而继承性和多态性都是语法问题。

继承性:避免我们定义重复的方法和重复的变量。

建议一个模块写一个类。

继承的简单例子:

#nine/c4.py
class Human():
    sum = 0
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def get_name(self):
        print(self.name)

#nine/c3.py
from c4 import Human

class Student(Human):
    def do_homework(self):
        print('homework')

print(Student.sum)      #通过类调用类变量
student1 = Student('julia', 18)     #继承至父类Human的__init__,需要录入name和age两项
print(student1.sum)     #通过对象的方式调用类变量
print(student1.name)    #变量继承
student1.get_name()     #方法继承

#0
#0
#julia
#julia

子类没有sum,由于其继承了父类的sum,所以能够打印出来。

python的继承有一个特点是:可以多继承(一个子类继承多个父类)

子类中定义实例变量,子类中调用父类的构造函数(不推荐):

#nine/c4.py
class Human():
    sum = 0
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def get_name(self):
        print(self.name)

#nine/c3.py
from c4 import Human

class Student(Human):
    def __init__(self, school, name, age):
        self.school = school
        Human.__init__(self, name, age)   #子类中调用父类的构造函数,注意self要加上
        #self 必须加的原因是:这里我们使用类Human来调用方法init,但self本身表示的是当前调用方法的实例对象;
        #像我们之前的方式,用对象 student1 来调用方法,就不用传入 self

    def do_homework(self):
        print('homework')

student1 = Student('西安小学','julia', 18)     #继承Human,因此必须传入参数,不传报错
print(student1.school)
print(student1.name)

#西安小学
#julia

不推荐原因:尽管我们在子类中实现了父类构造函数的调用,但是Human.__init__(self, name, age) 中 Human 是一个类,为抽象的,init方法是实例方法,使用一个类调用实例方法是说不通的。但是Python是灵活的,这种方式是可以运行的
一个类去调用一个实例方法实际上是不正确的。

from c3 import Human
class Student(Human):
    def __init__(self,school,name,age):
        self.school = school
        Human.__init__(self,name,age)    #调用父类初始化要加self
    def do_homework(self):
        print('done')

student1 = Student('春田花花幼儿园','tai',18)    #调用构造函数不需要加self
student1.do_homework()    #通过对象调用不用写self

实例化的时候调用构造函数时是python内部构造机制自动调用构造函数,它会帮助我们补全前面的self;
而在子类__init__中调用父类的__init__是自己通过类(而不是实例化的对象)去调用,python不会帮你补全前面的self。
如果通过对象调用,self的实质就是实例对象,所以不要写self。

验证:

from c3 import Human
class Student(Human):
    def __init__(self,school,name,age):
        self.school = school
        Human.__init__(self,name,age)
    def do_homework(self):
        print('done')

student1 = Student('春田花花幼儿园','tai',18)
Student.do_homework()        #通过类去调用实例方法
#Traceback (most recent call last):
# File "c1.py", line 13, in <module>
#    Student.do_homework()
#TypeError: do_homework() missing 1 required positional argument: 'self'    
#报错,缺少self。

开闭原则:对代码扩展开放,对代码更改关闭。

子类方法调用父类方法:super关键字

from c4 import Human

class Student(Human):
    def __init__(self, school, name, age):
        self.school = school
        super(Student, self).__init__(name, age)        #目前主流方法

    def do_homework(self):
        print('homework')

student1 = Student('西安小学','julia', 18)     #继承Human,因此必须传入参数name, age
print(student1.school)
print(student1.name)

#西安小学
#julia

当子类与父类的方法重名时,不会报错,而是会按照子类执行;

这时候我们可以使用super来执行父类的方法;因此super 不仅仅用于构造函数,还可以用于普通的实例方法:

#nine/c4.py
class Human():
    sum = 0
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def get_name(self):
        print(self.name)

    def do_homework(self):
        print('This is a parent method')

#nine/c3.py
from c4 import Human

class Student(Human):
    def __init__(self, school, name, age):
        self.school = school
        super(Student, self).__init__(name, age)

    def do_homework(self):
        super(Student, self).do_homework()
        print('homework')

student1 = Student('西安小学','julia', 18)     #继承Human,因此必须传入参数name, age
student1.do_homework()

#This is a parent method
#homework

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值