Python入门基础(6)——面向对象

1、类与实例

(1)类与对象

类就是一个模板,里面有多个函数(也称为方法)和变量,每个函数实现了一些功能,而对象则是根据类模板创建的实例,通过实例对象可以执行类中的函数

#创建类
class Foo:
    def bar(self):    #类中的函数,第一个参数一定是self(当然,不一定是self这个词)
        pass          #函数功能, 这里省略
    def hello(self,name):
        print('I am %s' %name)
obj = Foo()           #根据Foo类创建对象obj,创建对象的时候,要记得后面加括号
obj.Bar()             #执行Bar方法
obj.Hello('july')     #执行Hello方法 

注意:新创建的类中第一个参数一定是一个指向对象自身的参数(一般都是用self指代,但是你也可以指定别的词),这里的self就相当于java中的this

# 创建类
class Foo:
    # 这里我们可以创建一个类级别的变量
    # 它不会随着由此类创建的变量而变化,其实就是全局变量
    name = 'Jan'
 
    def hello(july, name): # 我这里把self改成了july,
        # 但是只要它作为第一参数的位置没变,它依旧是Foo Class的自我指代
        print('you are %s' %july.name)
        print('I am %s' %name)
        print('\n')
 
# 根据Foo创建的对象
obj1 = Foo()
obj2 = Foo()
obj1.hello('August')    #这里会输出“you are Jan  I am  August”
obj2.hello('July')      #这里会输出“you are Jan  I am  July”

注意:全局变量与局部变量(函数内定义的变量),如果名称相同,局部变量就会对全局变量进行覆盖(这里有过编程经历的人都知道,这里就不再细说了)

(2)构造函数

创建对象的时候,用来初始化对象的,默认自动执行,完成一些初始化的动作

# 创建类
class Foo:
    
    def __init__(self):#这就是构造函数,它的职责是在模型创建的初期,就完成一些动作
        #简单的说就是,自定义的初始化步骤:
        #同样,它需要self来指代本身这个class
        self.name='Jan'
 
    def hello(self, name):
        print('you are %s' %self.name)
        print('I am %s' %name)
        print('\n')

#当你创建一个Foo类的时候,init会被自动跑一遍:
obj = Foo()
# 在我们的例子中,我们默认给self自己的name变量,赋值为’JAN‘
# 此刻,当我们调用Foo的hello()方法时,hello自己的name变量,被赋值为'July'
obj.hello('July')

2、访问限制

(1)私有变量的定义

有时候,我们要对类的数据变量进行透明化,也就是让外部无法访问类内部的变量,或是可以访问,但是不能更改,这就相当于java中的private、public和protect,但是Python中没有直接定义类型的规矩,而是通过书写规则来定义。

当你不想让类中的内部属性被外部访问的时候,可以把属性的名称前加上两个下划线__(看着像一个,其实是两个下划线哦),在Python中,如果实例的变量名以双下划线__开头,就变成了一个私有变量,只有内部可以访问,而外部不能访问(当然了,你可以通过调用实例的方法,而这个方法再调用这个私有变量,这样就可以访问了),下面看一个例子吧:

# 首先这个类中,定义了几个变量,但是都是共有变量,外部可以直接访问和改变
class Student:
    
    def __init__(self, name, age):
        self.name = name
        self.age = age  
    
    def detail(self):
        print(self.name)
        print(self.age)

LiLei = Student('LiLei', 12)
LiLei.age = 20      #这里直接访问LiLei的年龄,然后将其由12改为了20
LiLei.detail()      #这里直接输出“Lilei    20”

上面这个例子是一个public类性的变量,外部可以访问,可以更改

class Student:
    
    def __init__(self, name, age):
        self.__name = name     #这里对变量进行了封装,添加下划线,变成了私有变量
        self.__age = age  
    
    def detail(self):
        print(self.__name)
        print(self.__age)

LiLei = Student('LiLei', 12)
LiLei.__age = 20        
LiLei.detail()           #执行的话,你会发现依然会输出12,上面的赋值没有执行

其实,这里我有一个疑惑,那就是LiLei.__age = 20,既然没有被执行,那么为什么没有报错呢,结果很流畅:


如果有读者看到,可以在下面的评论中为我解惑,当然了,如果以后我找到答案啦,也会进行更新的

(2)私有变量的访问和修改

当然成为私有变量以后,不是说就不能访问和更改了,我们可以在类的内部设置函数,通过对象的函数来访问和修改:

class Student(object):

    def get_name(self):     #访问name私有变量
        return self.__name

    def get_age(self):
        return self.__age   #访问age私有变量
    
    def set_age(self, age): #设置age的值
        self.__age = age

3、面向对象的三大特性

(1)封装

封装本身是指:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别,封装就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。

其实上面的私有变量设置就是一种封装,让你不能修改里面的值,也不能访问内部的值,要访问可以,只能通过调用我的对象接口,至于我内部是怎么返回的,怎么修改的,你都不知道,API接口大家都应该知道,那就是一种封装。

再不懂的话,可以想象电脑,电脑对很多人来说,都是一个黑盒子,我们知道怎么用,但是不知道它内部是怎么实现的,它怎么一按电源开关,就会显示画面呢?这其实就是一种封装,将各种功能组合封装成一个盒子——电脑,然后你可以各种按键(也就是调用内部的API接口)来实现你想要的功能,一个熟练的白领看起来相当熟悉电脑的操作,但是其实他可能对电脑内部的原理一无所知。

封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,以特定的访问权限来使用类的成员。

在不懂就直接看百度百科:封装是什么?

class Student:
    # 假定我们初始化一个Student类的时候要做的就是,记录下每个学生的名字和年龄
    def __init__(self, name, age):
        self.name = name
        self.age = age  
    # 至此,我们用self指代student本身,并用name和age存下了他们的年龄和名字

#此时,我们新建一个学生
obj1 = Student('July', 18)
print(obj1.name)    # 直接调用obj1对象的name属性
print(obj1.age)     # 直接调用obj1对象的age属性
obj2 = Student('Aug', 73)
print(obj2.name)    # 直接调用obj2对象的name属性
print(obj2.age)     # 直接调用obj2对象的age属性

上面的例子中,我们把一些数据信息和函数方法封装成一个学生对象,如果要访问其信息,只要调用该学生对象的属性或方法即可,其实封装是一种理论和概念性的东西,现在不理解也无所谓,因为其实大多数时间,你都在不自觉地运用封装理念

(2)继承

继承的概念,这里就不再多介绍了,不会的可以去百度,这里列举一下继承的注意事项:

  • 子类可以继承父类的内容(父类有的,子类都有)
  • 子类如果对父类的变量或者函数方法不满意,可以进行重载覆盖掉(这里是覆盖,而不是删除,因为你可以调用自己的新方法,也可以调用父类的旧方法)
  • 子类可以有自己独特的方法或者变量,区别于父类,龙生九子,九子更不同,说的就是这个道理。

下面举个例子:

# 我们首先创建一个学生类,这个类是所有学生的父类
class Student:
    
    def __init__(self, name, age):      #构造方法
        self.name = name
        self.age = age  
    
    def detail(self):                   #一个普通方法
        print(self.name)
        print(self.age)

# 然后,我们创建一个小学生类
class PrimaryStudent(Student)             #因为是继承于学生类,所以我们写在括号内
                                          # 这里我们可以不写构造函数,于是我们就是直接沿用Student类的构造函数
    def qingchun(self):                   # 我们有一些新的独有的方法,会被叠加起来
        print('我的青春,我做主!')
    
# 接下来,我们创建一个大学生类
class CollegeStudent(Student):
    def __init__(self, name, age, gf):     #改写一下构造函数,这样父类的构造方法会被覆盖掉
        self.name = name
        self.age = age
        self.gf = gf
    def CS_detail(self):                  #大学生类独特方法
        print(self.gf)


obj1 = PrimaryStudent('小王', 7)
obj1.qingchun()                          # 独有的方法
obj1.detail()                            #继承与爸爸的方法

obj2 = CollegeStudent('王思聪', 29, '张雨馨')
obj2.detail()
obj2.CS_detail()

注意:通过上面,大家可以看到Python中,继承父类写法很简单,直接将父类写入括号内就可以了

  • 如果我们继承多个父类,而每个父类中都含有一个相同的方法,那么我们调用的时候,应该怎么调用呢?
class D(object):

    def bar(self):
        print('D.bar')


class C(D):

    def bar(self):
        print('C.bar')


class B(D):

    pass

class A(B, C):

    pass

a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了

在上面的例子中,A继承来B和C,而B和C又继承了D,如果你要调用一种方法,那么B中没有,你要去C中寻找,还是D中,如果去D中寻找,那就是深度优先了(顺着B一路找到低,没有再去找C),如果是去C中寻找,那就是广度优先了(先是同级查找,没有,再往深一层查找)

现在都是广度优先!

(3)多态

Python不支持多态,并且也用不到多态,多态的概念是应用与Java和C#这一类强类型语言中的,而Python是若类型语言,从变量的定义中就可以看出来,它对于变量的声明和创建根本就没有指定变量类型,而是根据变量的赋值来定义的(你看多么随意啊,格式非常随意,对于类型管理比较松)

知乎上对于多态的理解:多态知多少

百度上这个介绍也不错:谈谈你对多态的理解

4、获取对象的信息

(1)获取对象的类型——type()方法

对于type()方法,之前已经讲过了,这里就不再多说了

type("1234") == type("abd")      #输出为true,表明都是字符串

(2)类型是否包含

class A:
    pass
class B(A):
    pass
class C(B):
    pass

k=A()
g=B()
y=C()

isinstance(y, C)   #判断一个对象是否是某种类型(包括继承关系),回答是true

isinstance(y, B)   #因为C是继承自B的,所以返回值依然是true

isinstance('a', str)   #也可以判断类型,就是判断‘a’是否是str类型(其实就继承关系),答案当然是肯定的,所以返回值为true

(3)获取对象的所有属性和方法——dir()

使用dir()函数,它会返回一个包含字符串的list(里面列举了所有的属性和方法),下面举个例子:

print(dir("123"))

输出结果:


因为结果太长了,这里就不一一显示出来了。

注意:类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回字符串长度,而我们一般用的len()方法其实本质就是调用的__len__()方法,也就是说‘124’.__len__()与len(‘124’)是等价的

(4)操作对象的状态——getattr()、setattr()和hasattr()

hasattr(object,atr)——判断对象object是否具有属性atr(然后根据这个判断,你就可以做很多事情了)

getattr(object,atr)——获取对象object的属性atr的值(当然,前提你得知道这个对象有这个属性)

setattr(object,atr)——通过上面两步,你得知了对象的属性值,如果你觉得不满意,可以直接对其进行设置

#下面讲述一下这三个方法的使用实例,以后估计会经常用到
class MyObject:
    def __init__(self):
        self.x = 9         #定义一个普通变量
        self.__y = 10      #定义一个私有变量
    def power(self):
        return self.x * self.x

obj = MyObject()           #上面首先创建了一个方法和对象,然后可以用这三个方法对其进行查询和更改了

hasattr(obj, 'x')        #有木有属性'x',我们可以看到上面的方法中,是有x属性的,所以返回值为true

hasattr(obj, '__y')      #外部访问私有变量是行不通的,所以这里会返回False

hasattr(obj, 'y')        # 有属性'y'吗? 答案是显然没有的

setattr(obj, 'y', 19)    # 设置一个属性'y',既然没有我们就可以设置一个,后面的值就是y的值

getattr(obj, 'y')        # 获取y的值,你会发现,它的返回结果是19,正好是你设置的值

getattr(obj, 'z', 404)   # 获取属性'z',如果不存在,返回默认值404,这种情况还是经常发生的,因为很多时候,我们不知道它有没有这个属性

hasattr(obj, 'power')    # 有属性方法'power'吗?  这里不仅可以查询属性变量,还有属性方法和函数

getattr(obj, 'power')    # 获取属性方法'power',这里获得属性,不会直接告诉你方法内容,下面我会显示一些内容

fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn

fn()                     # 调用fn()与调用obj.power()是一样的

获得属性方法power的结果输出:


注意:私有变量利用上面三个方法是访问不到的,你用hassttr()查询,即便有,也会显示False,后面的两个方法更加没有了,所以调用和改变私有变量的方法,只能通过该对象本身自带的函数方法实现


5、实例属性和类属性

啥也不说了,直接上实例:

class Student(object):
    name = 'Student'       #类属性
    def __init__(self, name):
        self.name = name   #对象属性赋值,每个对象都会有不同的对象属性值,但是它们的类属性值都是一样的

s = Student() # 创建实例s
print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性

s = Student('Bob')
print(s.name)      #查询s对象的name属性,首先会显示s的属性:Bob
print(Student.name)  #当然这并不是说类的name属性也被修改了,这里依然会显示:Student

del s.name    #删除实例对象的name属性
print(s.name) #再次调用s.name,由于实力对象的name属性已经删除了,所以类的name属性就会显示出来:Student

s.score = 90    #给对象s绑定属性score,注意这里的score只是对象s的,而不是类的,即便再次创建一个类对象,也不会有score
print(s.score)

从上面的例子可以看出来:如果对象属性和类属性名是一样的话,那么调用的时候,类属性会被屏蔽掉(就好像继承类中的方法重载一样),当你删除对象属性以后,再次调用的时候,显示的依然会是类属性







猜你喜欢

转载自blog.csdn.net/yuangan1529/article/details/80680249