《老鸟python 系列》视频上线了,全网稀缺资源,涵盖python人工智能教程,爬虫教程,web教程,数据分析教程以及界面库和服务器教程,以及各个方向的主流实用项目,手把手带你从零开始进阶高手之路!点击 链接 查看详情




构造函数和访问控制

阅读:227569004    分享到

在面向对象编程中,构造函数和访问控制是我们经常用到的,并且是面向对象很重要的特性。

构造函数

虽然我们需要类的所有实例拥有共同的属性, 但是我们不想让他们拥有共同的属性值,在对象创建后,我们可以调用一个函数来修改这些属性值。

class Human():
    def initdata(self, myname, myage):
        self.name = myname
        self.age = myage

    def speak(self):
        print("大家好,我是%s;我的年龄是%s" % (self.name, self.age))

luren_a = Human()
luren_a.initdata("ruhua", 18)
luren_a.speak()

luren_b = Human()
luren_b.initdata("zhaoritian", 17)
luren_b.speak()

我们发现上面那种方法重复繁琐,Python 语言可以通过定义一个名称为 __init__ 的函数,该函数叫做构造函数,在我们创建对象的时候,Python 解释器会自动调用构造函数。构造函数也是类的成员函数,至少也要拥有一个参数,我们知道第一个参数是用来接收调用对象本身的,在构造函数内部,我们可以把各种属性绑定到该参数上。

class Human():
    def __init__(self, myname, myage):
        self.name = myname
        self.age = myage

    def speak(self):
        print("大家好,我是%s;我的年龄是%s" % (self.name, self.age))

luren_a = Human("ruhua", 18)     # Python 解释器会自动调用构造函数,并把 luren_a 作为第一个实参传给构造函数
luren_a.speak()

luren_b = Human("xingxing", 17)  # Python 解释器会自动调用构造函数,并把 luren_b 作为第一个实参传给构造函数
luren_b.speak()

由于在构造函数中我们把调用构造函数的对象本身传过去,然后通过这个对象初始化属性,这样一来,可以实现每个对象拥有不同的属性值。

class Human():
    def __init__(self, myname):
        self.name = myname

luren_a = Human("ruhua")
luren_b = Human("xingxing")

print(luren_a.name)  # luren_a 对象的 name
print(luren_b.name)  # luren_b 对象的 name

假如我们定义一个圆类,对于所有的圆类产生的对象都有 π 这个属性,并且该属性对所有圆对象的值都是一样的, 我们没必要把 π 这个属性在每个对象创建的时候在构造函数内重新赋值,因为他们是一样的,我们可以把该属性定义在构造函数外面,我们称为该属性是属于类的(也就是所有对象共有一个)。

class Circle():
    PI = 3.1415926
    def __init__(self, radius):
        self.radius = radius

    def showarea(self):
        print(self.radius * self.PI)

redcircle = Circle(3)
redcircle.showarea()    # 用的是类的属性 PI

bluecircle = Circle(5)  # 用的是类的属性 PI
bluecircle.showarea()

Circle.PI = 1           # 可以通过类改变属性 PI
print(redcircle.PI)     # 用的是类的属性 PI
print(bluecircle.PI)    # 用的是类的属性 PI

Python 如何实现让所有对象共享一个属性的呢?很简单,当 Python 发现类内存在属于类的属性时,只需要在每个对象初始化时,让对象中同名属性指向类属性即可。 因此,我们试图通过某个对象修改属于类的属性,则对其它对象不起作用,因为修改的只是这个对象属性本身的指向,所有的其它对象属性还在指向这个属于类的属性。

class Circle():
    PI = 3.1415926
    def __init__(self, radius):
        self.radius = radius

redcircle = Circle(3)
bluecircle = Circle(5)
redcircle.PI = 1      # 只是改变了 redcircle 对象的属性 PI 指向

print(redcircle.PI)   # 1
print(bluecircle.PI)  # bluecircle 对象的属性 PI 还在指向 Cirle 类的属性 PI
print(Circle.PI)      # Circle 类的属性 PI 只能通过 Cirle 类来改变

访问控制

我们在进行面向对象编程时,最好不要直接通过对象来访问(读或写)属性,而是通过类提供的函数进行读写属性操作,要养成良好的编程习惯。

class Human():
    def __init__(self, myname):
        self.name = myname

    def getname(self):
        return self.name

    def setname(self, newname):
        self.name = newname

luren_a = Human("ruhua")
print(luren_a.getname())        # 访问属性
luren_a.setname("wanghuahua")   # 修改属性
print(luren_a.getname())

人的天性都是非自觉的,程序员也是人,所以需要约束,我们刚刚讲过最好不要通过实例直接访问类的属性, 那只是要求程序员自觉遵守,所以我们可以强制性约束,为了让内部属性不被外部访问, 我们可以在属性的名称前加上两个下划线 __,在 Python 中,实例的变量名如果以 __ 开头,就变成了一个私有变量(private),私有变量只有在类的内部可以访问(读写),在类的外部不能访问(读写)。

class Human():
    def __init__(self, myname):
        self.__name = myname

    def getname(self):
        return self.__name      # 类的内部可以访问

    def setname(self, newname):
        self.__name = newname   # 类的内部可以访问

luren_a = Human("ruhua")
print(luren_a.getname())
luren_a.setname("wanghuahua")
print(luren_a.getname())

print(luren_a.__name)          # 报异常,因为类的外部无法访问
luren_a.__name = "wanghuahua"  # 报异常,因为类的外部无法访问

有时候,我们会遇到以单下划线开头的变量,这样的实例变量在外部是可以访问的,我们最好不要去访问,这是一个不成文的约定。

class Human():
    def __init__(self, myname):
        self._name = myname

luren_a = Human("ruhua")
print(luren_a._name)  # 类的外部可以访问 _name,但最好不要访问,要遵守约定

Python 本身不进行访问控制的语法检查,以双下划线开头的变量不能访问的原因, 仅仅是因为 Python 在类内部把所有以 __ 开头的变量进行了更名,我们如果知道更改后的属性名,就可以在外部进行访问该属性,注意不同版本的 Python 解释器可能会把该变量改成不同的变量名。

class Human():
    def __init__(self, myname):
        self.__name = myname

luren_a = Human("ruhua")
print(luren_a._Human__name)    # Python 3.7 版本把名字改成了 _Human__name
Human.__name = "wanghuahua"
print(luren_a.__name)          # Python 只对类内部 __开头的变量做改名,所以可以访问

# 我们可以通过 id 函数知道它们并不是一个变量
print(id(luren_a.__name))
print(id(luren_a._Human__name))

本节重要知识点

构造函数如何定义

Python 实现访问控制的方法

作业

著名公司(某软)笔试题:给某个公司设计一个职工类,所有职工都是中国人,大部分是男性,类里面有属性(姓名,性别,国籍,月薪,婚否), 类里面有方法(根据姓名(可以重名)获得职工的年薪,对所有职工按年薪做排序,获取男性员工和女性员工所占人数的比例,获取已结婚的中国籍员工人数)。


如果以上内容对您有帮助,请老板用微信扫一下赞赏码,赞赏后加微信号 birdpython 领取免费视频。


登录后评论

user_image
1不2
2020年5月31日 16:57 回复

明白了,不过一般用__ini__就可以了吧,不需要在实现__new__方法


user_image
Michael李登淳
2020年5月17日 21:33 回复

在C++中的同类名的构造函数,其功能是相当于_init还是相当于__new__?


user_image
海克斯科技大冰锤
2020年4月17日 06:46 回复

难道说__new_和_init__的区别在于前者可以访问静态变量而后者不可以??


user_image
Gaulermat
2019年6月13日 17:10 回复

为什么很多人认为Python容易,甚至高中生都要学,然后参加考试。而熟练使用C/C++,Java,Go的我,认为Python的难度远大于前三者。各种各样的语法糖充斥这Python,庞大的约定机制,作者似乎脑子想到一个什么约定就去毫无道理地实现它,完全没有前面三者的简洁。再加上解释型语言的难调试性,IDE的不友好性,让我对Python简直深恶痛绝。


user_image
江振雄
2018年11月26日 09:40 回复

昨天看python面向对象编程中有提过这个,不过很少用,new方法一般是用在元编程