访问可见性问题

对于上面的代码,有C++、Java、C#等编程经验的程序员可能会问,我们给Student对象绑定的nameage属性到底具有怎样的访问权限(也称为可见性)。因为在很多面向对象编程语言中,我们通常会将对象的属性设置为私有的(private)或受保护的(protected),简单的说就是不允许外界访问,而对象的方法通常都是公开的(public),因为公开的方法就是对象能够接受的消息。在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头,下面的代码可以验证这一点。

class Test:

    def __init__(self, foo):
self.__foo = foo def __bar(self):
print(self.__foo)
print('__bar') def main():
test = Test('hello')
# AttributeError: 'Test' object has no attribute '__bar'
test.__bar()
# AttributeError: 'Test' object has no attribute '__foo'
print(test.__foo) if __name__ == "__main__":
main()

但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来“妨碍”对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。之所以这样设定,可以用这样一句名言加以解释,就是“We are all consenting adults here”。因为绝大多数程序员都认为开放比封闭要好,而且程序员要自己为自己的行为负责。

class Test:

    def __init__(self, foo):
self.__foo = foo def __bar(self):
print(self.__foo)
print('__bar') def main():
test = Test('hello')
test._Test__bar()
print(test._Test__foo) if __name__ == "__main__":
main()

在实际开发中,我们并不建议将属性设置为私有的,因为这会导致子类无法访问(后面会讲到)。所以大多数Python程序员会遵循一种命名惯例就是让属性名以单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重。这种做法并不是语法上的规则,单下划线开头的属性和方法外界仍然是可以访问的,所以更多的时候它是一种暗示或隐喻,关于这一点可以看看我的《Python - 那些年我们踩过的那些坑》文章中的讲解。

面向对象的支柱

面向对象有三大支柱:封装、继承和多态。后面两个概念在下一个章节中进行详细的说明,这里我们先说一下什么是封装。我自己对封装的理解是“隐藏一切可以隐藏的实现细节,只向外界暴露(提供)简单的编程接口”。我们在类中定义的方法其实就是把数据和对数据的操作封装起来了,在我们创建了对象之后,只需要给对象发送一个消息(调用方法)就可以执行方法中的代码,也就是说我们只需要知道方法的名字和传入的参数(方法的外部视图),而不需要知道方法内部的实现细节(方法的内部视图)。

练习

练习1:定义一个类描述数字时钟

class Clock(object):
"""数字时钟""" def __init__(self, hour=0, minute=0, second=0):
"""初始化方法 :param hour: 时
:param minute: 分
:param second: 秒
"""
self._hour = hour
self._minute = minute
self._second = second def run(self):
"""走字"""
self._second += 1
if self._second == 60:
self._second = 0
self._minute += 1
if self._minute == 60:
self._minute = 0
self._hour += 1
if self._hour == 24:
self._hour = 0 def __str__(self):
"""显示时间"""
return '%02d:%02d:%02d' % \
(self._hour, self._minute, self._second) def main():
clock = Clock(23, 59, 58)
while True:
print(clock)
sleep(1)
clock.run() if __name__ == '__main__':
main()

练习2:定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法。

from math import sqrt

class Point(object):

    def __init__(self, x=0, y=0):
"""初始化方法 :param x: 横坐标
:param y: 纵坐标
"""
self.x = x
self.y = y def move_to(self, x, y):
"""移动到指定位置 :param x: 新的横坐标
"param y: 新的纵坐标
"""
self.x = x
self.y = y def move_by(self, dx, dy):
"""移动指定的增量 :param dx: 横坐标的增量
"param dy: 纵坐标的增量
"""
self.x += dx
self.y += dy def distance_to(self, other):
"""计算与另一个点的距离 :param other: 另一个点
"""
dx = self.x - other.x
dy = self.y - other.y
return sqrt(dx ** 2 + dy ** 2) def __str__(self):
return '(%s, %s)' % (str(self.x), str(self.y)) def main():
p1 = Point(3, 5)
p2 = Point()
print(p1)
print(p2)
p2.move_by(-1, 2)
print(p2)
print(p1.distance_to(p2)) if __name__ == '__main__':
main()

说明:本章中的插图来自于Grady Booch等著作的《面向对象分析与设计》一书,该书是讲解面向对象编程的经典著作,有兴趣的读者可以购买和阅读这本书来了解更多的面向对象的相关知识。

Python3中_和__的用途和区别的更多相关文章

  1. python _、__和__xx__的区别

    python _.__和__xx__的区别 本文为译文,版权属于原作者,在此翻译为中文分享给大家.英文原文地址:Difference between _, __ and __xx__ in Pytho ...

  2. 接口测试基础——第7篇 Python中_、__、__func__之间的区别

    今天的东西很少,主要是给自己做个笔记,顺便帮大家普及一下Python中的边角知识: 1.if __name__ == "__main__"是什么意思 答:一个.py文件,如果是自身 ...

  3. python中 _、__、__xx__() 区别及使用场景

    1.访问权限(private.public)与继承方式(只有public继承) 在面向对象编程语言中,类的属性与方法都会设置访问控制权限,从而满足我们的设计需求.一般而言,我们通常会将对象的属性设置为 ...

  4. 对比Python中_,__,xx__xx

      对比Python中_,__,xx__xx _ 的含义 不应该在类的外面访问,也不会被from M import * 导入. Python中不存在真正的私有方法.为了实现类似于c++中私有方法,可以 ...

  5. python中_、__、__xx__(单下划线、双下划线等)的含义

    (1)_xxx "单下划线 " 开始的成员变量相当于私有变量,也叫做保护变量,意思是只有类实例和子类实例能访问到这些变量,需通过类提供的接口进行访问(可以定义有点像java中的ge ...

  6. python _、__和__xx__的区别(转)

    本位转载自:http://www.cnblogs.com/coder2012/p/4423356.html "_"单下划线 Python中不存在真正的私有方法.为了实现类似于c++ ...

  7. Oracle_Database_11g_标准版_企业版__下载地址_详细列表

    Oracle_Database_11g_标准版_企业版__下载地址_详细列表 Oracle Database 11g Release 2 Standard Edition and Enterprise ...

  8. python3中的编码与解码(超好理解)

    编码和解码是针对数据而言的,数据能干什么呢?无非就是用来显示,储存和传输的: 储存和传输数据当然是希望数据越小越好,所以发明了utf-8这种数据编码显示:它智能将英文用一个字节表示,欧洲的字符用两个字 ...

  9. 转 python3中SQLLIT编码与解码之Unicode与bytes

    #########sample########## sqlite3.OperationalError: Could not decode to UTF-8 column 'logtype' with ...

随机推荐

  1. HugePages概述--翻译自19C文档

    翻译自: https://docs.oracle.com/en/database/oracle/oracle-database/19/unxar/administering-oracle-databa ...

  2. LoadRunner生成测试报告

     loadrunner笔记(三):设置.运行场景和生成测试报告   //上一篇的代码有点问题,问题出在 web_reg_find()函数中,这个函数简单的说是搜索下一步操作的请求对象(html)页面中 ...

  3. Anaconda配置Python开发环境

    Anaconda介绍 Anaconda 是在 linux.windows 和 mac os x 上执行 Python/R 数据分析和机器学习的最简单的方式并且它是开源的.它在全球拥有超过 1, 100 ...

  4. dubbo的初探

    1.RPC  基本概念1.1 RPC  协议(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议 ...

  5. pyqt pyside qcombobox disable wheel scrolling

    pyqt pyside qcombobox disable wheel scrolling import sys from PyQt5 import QtCore, QtWidgets import ...

  6. Css案例整理

    1.实现两个div并排显示 案例:checkbox的标题和内容需要并排显示,checkbox竖向排列 <head> <style type="text/css"& ...

  7. spring配置和映射文件

    配置 <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www. ...

  8. HDU 5451——递推式&&循环节

    题意 设 $y = (5+2\sqrt 6)^{1+2^x}$,给出 $x, M$($0\leq x \leq 2^{32}, M \leq 46337$),求 $[y]\%M$. 分析 由通项推递推 ...

  9. BZOJ 1458 / Luogu P4311 士兵占领 (上下界最小流 / 直接最大流)

    做法1:上下界最小流 先来一发上下界最小流,思路比较暴力,就是把行和列看作n+mn+mn+m个点,(i,j)(i,j)(i,j)如果能占领就从第iii行向第jjj列连一条边,上界为1下界为0;然后从s ...

  10. 014_linux驱动之_信号符号名、描述和它们的信号值

    符号名 信号值 描述 是否符合POSIX SIGHUP 1 在控制终端上检测到挂断或控制线程死 亡 是 SIGINT 2 交互注意信号 是 SIGQUIT 3 交 互中止信号 是 SIGILL 4 检 ...