微信公众号:码农充电站pro
个人主页:https://codeshellme.github.io
那些能用计算机迅速解决的问题,就别用手做了。
—— Tom Duff
目录
上一节 我们介绍了@H_502_25@Python 面向对象的相关概念,我们已经知道@H_502_25@类与对象是@H_502_25@面向对象编程中非常重要的概念。
类就是一个@H_502_25@模板,是抽象的。对象是由类创建出来的实例,是具体的。由同一个类创建出来的对象拥有相同的@H_502_25@方法和@H_502_25@属性,但属性的值可以是不同的。不同的对象是不同的实例,互不干扰。
1,类的定义
如下,是一个最简单的类,实际上是一个@H_502_25@空类,不能做任何事情:
class People:
pass
在Python 中定义一个类,需要用到@H_502_25@class 关键字,后边是@H_502_25@类名,然后是一个@H_502_25@冒号:,然后下一行是类中的代码,注意要有@H_502_25@缩进。
2,创建对象
@H_502_25@People 虽然是一个@H_502_25@空类,但依然可以创建对象,创建一个对象的语法为:
对象名 = 类名(参数列表)
参数列表是跟@H_502_25@__init__ 构造方法相匹配的,如果没有编写@H_502_25@__init__ 方法,创建对象时,就不需要写参数,如下:
>>> p = People()
>>> p
<__main__.People object at 0x7fd30e60be80>
>>>
>>> p1 = People()
>>> p1
<__main__.People object at 0x7fd30e60be48>
@H_502_25@p 和 @H_502_25@p1 都是@H_502_25@People类的对象。@H_502_25@0x7fd30e60be80 是@H_502_25@p 的地址,@H_502_25@0x7fd30e60be48 是@H_502_25@p1 的地址。可以看到不同的对象的地址是不同的,它们是两不同的实例,互不干扰。
3,属性
类中可以包含@H_502_25@属性(@H_502_25@类中的变量),创建出来的对象就会拥有相应的属性,每个对象的属性的值可以不同。
>>> p = People()
>>> p.name = '小明' # 添加 name 属性
>>> p.sex = '男' # 添加 sex 属性
>>> p.name # 访问对象的属性
'小明'
>>> p.sex # 访问对象的属性
'男'
虽然在技术上可以这样做,但是一般情况下,我们并不这样为对象添加属性,这样会破坏类的@H_502_25@封装性,使得代码混乱,不利于维护。
当访问一个不存在的属性时,会出现异常:
>>> p.job # 一个不存在的属性
Traceback (most recent call last):
File "<stdin>",line 1,in <module>
AttributeError: 'People' object has no attribute 'job'
我们一般会在@H_502_25@__init__ 方法中为类添加属性并赋值。
4,@H_502_25@__init__ 方法
在Python 的类中,以双下划线@H_502_25@__开头和结尾的方法,被称为@H_502_25@魔法方法,每个魔法方法都有特定的含义。Python 为我们规定了一些魔法方法,让我们自己实现这些方法。
@H_502_25@__init__ 方法叫做@H_502_25@构造方法,用来初始化对象。Python 解释器会在生成@H_502_25@对象时,自动执行构造方法,而无需用户显示调用。
类中的所有@H_502_25@实例方法 方法,都至少有一个参数,就是@H_502_25@self。Python 中的@H_502_25@self 相当于C++ 和Java 中的@H_502_25@this 指针,都是代表当前对象。只是Python 中的@H_502_25@self 需要显示写在方法的第一个参数,而@H_502_25@this 指针则不需要写在方法参数中。
构造方法一般用于初始化对象的一些属性,构造函数可以不写,也可以只有一个@H_502_25@self 参数。
当构造函数只有一个@H_502_25@self 参数时,创建该类的对象时,不需要添加参数。当构造函数除了@H_502_25@self 参数还有其它参数时,创建该类的对象时,则需要添加相匹配的参数。
比如,我们定义一个@H_502_25@People 类,它有三个属性,分别是@H_502_25@name,@H_502_25@sex,@H_502_25@age:
class People:
def __init__(self,name,sex,age):
self.name = name
self.sex = sex
self.age = age
print('执行了 __init__ 方法')
def print_info(self):
print('people:%s sex:%s age:%s' % (
self.name,self.sex,self.age))
在这个@H_502_25@People 类中除了有一个@H_502_25@__init__ 方法外,还有一个@H_502_25@print_info 方法,每个方法中的都有@H_502_25@self 参数,并且是第一个参数,@H_502_25@self 代表当前对象。
在创建该类的对象时,需要传递匹配的参数(@H_502_25@self 参数不用传递):
>>> p = People('小明','男',18)
执行了 __init__ 方法
>>> p
<People.People object at 0x7feb6276bda0>
>>> p.print_info()
people:小明 sex:男 age:18
>>>
>>> p1 = People('小美','女',18)
执行了 __init__ 方法
>>> p1
<People.People object at 0x7fd54352be48>
>>> p1.print_info()
people:小美 sex:女 age:18
可以看到,在创建@H_502_25@p 和@H_502_25@p1 对象时,字符串@H_502_25@执行了 __init__ 方法 被打印了出来,而我们并没有显示调用该方法,说明@H_502_25@__init__ 方法被默认执行了。
对象@H_502_25@p 和@H_502_25@p1 是两个不同的对象,拥有相同的属性和方法,但是属性值是不一样的。两个对象互不干扰,对象@H_502_25@p 的地址为@H_502_25@0x7feb6276bda0,@H_502_25@p1 的地址是@H_502_25@0x7fd54352be48。
执行代码@H_502_25@p.print_info(),是调用@H_502_25@p 对象的@H_502_25@print_info() 方法,因为,在定义该方法的时候,只有一个@H_502_25@self 参数,所以在调用该方法的时候,不需要有参数。
5,私有属性和方法
私有属性
普通的属性,就像上面的@H_502_25@name,@H_502_25@sex和@H_502_25@age 属性,都是@H_502_25@公有属性,在类的外部都可以被任意的访问,就是可以用@H_502_25@对象.属性名的方式来访问属性,如下:
>>> p = People('小明',18)
执行了 __init__ 方法
>>> p.name # 访问属性
'小明'
>>> p.name = '小丽' # 修改属性
>>> p.name # 访问属性
'小丽'
这样就破坏了数据的@H_502_25@封装性,这种访问方式是不可控(会不受限制的被任意访问)的,不利于代码的维护,不符合面向对象的编程规范。
所以,通常我们会将类中的属性,改为@H_502_25@私有属性,就是不能以@H_502_25@对象.属性名 这样的方式访问类属性。
在Python 中,通过在属性名的前边添加双下划线@H_502_25@__,来将@H_502_25@公有属性变为@H_502_25@私有属性,如下:
#! /usr/bin/env python3
class People:
def __init__(self,age):
self.__name = name # 两个下划线
self.__sex = sex # 两个下划线
self._age = age # 一个下划线
print('执行了 __init__ 方法')
def print_info(self):
print('people:%s sex:%s age:%s' % (
self.__name,self.__sex,self._age))
这样就无法通过@H_502_25@对象.属性名的方式来访问属性了,如下:
>>> p = People('小美',18)
执行了 __init__ 方法
>>> p.__name # 出现异常
Traceback (most recent call last):
File "<stdin>",in <module>
AttributeError: 'People' object has no attribute '__name'
但是,Python 中这种@H_502_25@私有属性的方式,并不是真正的私有属性,Python 只是将@H_502_25@__name 转换为了@H_502_25@_People__name,即是在@H_502_25@__name 的前边加上了@H_502_25@_类名(@H_502_25@_People),我们依然可以这样访问@H_502_25@__name 属性:
>>> p._People__name
'小美'
但我们并不提倡这种方式,这会让代码变得混乱难懂。
可以注意到,@H_502_25@People 类中的@H_502_25@_age 属性是以单下划线开头的,这种以单下划线开头的属性是可以在类的外部被访问的:
>>> p._age
18
但是根据Python 规范,以单下划线开头的属性,也被认为是@H_502_25@私有属性,也不应该在类的外部访问(虽然在技术上是可以访问的)。
注意:以双下划线@H_502_25@__ 开头且结尾的属性@H_502_25@__xxx__,是@H_502_25@特殊属性,是公有的,可在类的外部访问
私有方法
私有方法与私有属性类似,也可以在方法名的前边加上双下划线@H_502_25@__,来将某个方法变成私有的,一般不需要被外部访问的方法,应该将其设置为@H_502_25@私有方法。
6,@H_502_25@set 和 @H_502_25@get 方法
为了数据的@H_502_25@封装性,我们不应该直接在类的外部以@H_502_25@对象.属性名的方式访问属性,那么如果我们需要访问类的属性该怎么办呢?
为了减少代码量,这里只为@H_502_25@__name 属性设置了这两个方法,代码如下:
#! /usr/bin/env python3
class People:
def __init__(self,age):
self.__name = name
self.__sex = sex
self._age = age
print('执行了 __init__ 方法')
def print_info(self):
print('people:%s sex:%s age:%s' % (
self.__name,self._age))
# set 和 get 方法
def set_name(self,name):
self.__name = name
def get_name(self):
return self.__name
>>> from People import People
>>> p = People('小美',18)
执行了 __init__ 方法
>>> p.get_name() # 获取 name 值
'小美'
>>> p.set_name('小丽') # 设置新的值
>>> p.get_name() # 再次获取name 值
'小丽'
因为这种@H_502_25@set 和 @H_502_25@get 方法,是由类的开发者提供的,是被开发者控制的。
类的开发者会根据需要,来控制类的使用者如何使用该类,即哪些类的属性和方法应该被使用者访问,以及如何被使用者访问。
如此,类的使用者就不能随便的访问类中的属性,这就达到了@H_502_25@封装的目的。
(完。)
推荐阅读:
Python 简明教程 --- 14,Python 数据结构进阶
Python 简明教程 --- 16,Python 高阶函数
Python 简明教程 --- 17,Python 模块与包
Python 简明教程 --- 18,Python 面向对象
欢迎关注作者公众号,获取更多技术干货。