面向过程编程:
1.导入各种外部库
2.设计各种全局变量
3.写一个函数完成某个功能
4.写一个函数完成某个功能
5.写一个函数完成某个功能
6. ................
7.写一个main函数作为程序入口
面向过程编程,很多重要的数据被放置再全局数据区,这样所有的函数都可以进行访问。而且每个函数都有自己的局部数据,将某些功能代码封装到函数中,就不必重复编写,仅需调用函数即可。从代码的组织形式来看就是根据业务逻辑从上到下垒代码 。
举个栗子:
假设我们有一辆汽车,我们知道它的速度(60km/h),以及A、B两地的距离(100km)。要算出开着这辆车从A地到B地花费的时间。
// 面向过程的方法
speed = 60.0
distance = 100.0
time = distance / speed
print time // 1.66666666667
speed1 = 60.0
distance1 = 100.0
time1 = distance1 / speed1
print time1 // 1.66666666667
distance2 = 200.0
time2 = distance2 / speed1
print time2 // 3.33333333333
speed2 = 150.0
time3 = distance1 / speed2
print time3 // 0.666666666667
time4 = distance2 / speed2
print time4 // 1.33333333333
面向对象编程:
1.导入各种外部库
2.设计各种全局变量
3.决定你要的类
4.给每个类提供完整一组操作
5.明确地使用继承来表现不同类之间的共同点
6.根据需要,决定是否写一个main函数作为程序入口
面向对象编程中,将函数和变量进一步封装成类,类才是程序的基本元素
,它将数据和操作紧密地连接再一起,并保护数据不会被外界的函数意外地改变。类和类的实例(也称为对象)是面向对象的核心概念,是和面向过程编程、函数式编程的根本区别。
举个栗子:
跟上述的面向过程的栗子相同,我们看看面向对象是怎样进行操作。
// 面向对象
class Car:
speed = 0
def drive(self, distance):
time = distance / self.speed
print time
car1 = Car()
car1.speed = 60.0
car1.drive(100.0)
car1.drive(200.0)
car2 = Car()
car2.speed = 150.0
car2.drive(100.0)
car2.drive(200.0)
面向对象是通过定义class类来定义
,简单的说,面向对象编程是只使用class类,在class类中封装、继承的功能,并且还可以构造要传入的参数。
class Dog(object):
"""创建一个类的名字为Dog, 类名规则是驼峰式命名"""
def __init__(self, name): // __init__(self) 初始化方法
self.name = name // 初始化变量
def eat(self):
"""定义一个吃的方法"""
print self.name
// 创建Dog类的时候, 会先执行__init__初始化方法, 首先传入name参数, 返回的dog是对象的实例, 实例可以调用类中的方法
dog = Dog('金毛')
dog.eat()
// 输出结果: 金毛
下面详细介绍一些 专业术语概念
:
1.类(class):用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法
。
2.实例:也称对象。通过类定义的初始化方法
,赋予具体的值,成为一个"实体"。
3.实例化:创建类的实例的过程或操作
。
4.实例变量:定义在实例中的变量,只作用于当前实例
。
5.类变量:类变量是所有公有的变量。类变量定义在类中,但在方法之外
。
6.数据成员:类变量、实例变量、方法、类方法、静态方法、属性等的统称。
7.方法:类中定义的函数
。
8.静态方法:不需要实例化就可以由类执行的方法
。
9.类方法:类方法是将本身作为对象进行操作的方法
。
10.方法重写:如果从父类继承的方法不能满足子类的需求,可以对父类的方法进行改写
。
11.封装:将内部实现包裹起来,对外透明,提供api接口进行调用的机制。
12.继承:将一个派生类继承父类的变量和方法。
13.多态:根据对象类型的不同以不同的方式进行处理。
class Dog(object):
"""创建一个类名Dog的类: 继承object表示的是新式类"""
description = '目前我们创建一个类的变量'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
# 定义构造的过程就是"实例化"
def eat(self):
"""创建一个吃的方法"""
print "{}正在吃吃锅包肉".format(self.name)
# 创建类的时候, 里面传入的参数, 取决于__init__方法中接收的参数, dog称为实例或称为对象
dog = Dog('哈士奇', 6, "男")
# 实例或对象.()方法
dog.eat() # 调用类里面的eat()方法, 输出结果:哈士奇正在吃吃锅包肉
print dog.name # 实例变量指的是实例本身拥有的变量.每个变量在内存中的位置是不一样的 输出结果:哈士奇
print dog.description # 类变量, 在类里面找到定义的变量 输出结果:目前我们创建一个类的变量
class Dog(object):
def __init__(self, name):
self.name = name
def eat(self):
print "{}正在吃吃锅包肉".format(self.name)
dog = Dog('哈士奇')
dog.eat() # 这种调用方法就是实例方法
静态方法由类调用,无默认参数。并且将实例方法参数中的self去掉,再方法定义上方加上@staticmethod,就成为静态方法。
静态方法属于类,但是和实例无关
。class Dog(object):
# 静态方法的使用
@staticmethod
def eat():
print "建议仅使用,'类名.静态方法', 调用方式"
Dog().eat() # 调用了类的方法, 仅在类中运行而不在实例中运行的方法
// 输出结果:建议仅使用,'类名.静态方法', 调用方式
类方法由类调用,采用@classmethod装饰,至少传入一个cls(代指类本身,类似self)参数
。执行类方法时,自动将调用该方法的类赋值给cls
。class Dog(object):
# 创建一个类方法
@classmethod
def eat(cls, name):
"""定义一个类方法"""
print "{}正在吃锅包肉".format(name)
dog = Dog()
dog.eat('哈士奇') # 可以使用实例名.类方法() 输出结果:哈士奇正在吃锅包肉
Dog.eat('哈士奇') # 类名.类方法() 输出结果:哈士奇正在吃锅包肉
封装是指将数据与具体操作的实现代码放在某个对象内部,外部无法访问。必须要先调用类的方法才能启动。
class Dog(object):
description = '类的封装'
def __int__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
print Dog.description //类变量, 再类里面找到定义的变量。 输出结果:类的封装
print description
// 报错信息如下
Traceback (most recent call last):
File "C:/Users/lh9/PycharmProjects/lvtest/apps/tests.py", line 891, in <module>
print description
NameError: name 'description' is not defined
当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)
。
class Animal(object):
"""定义一个基类(父类或超类)"""
def run(self):
"""基类有一个run方法"""
print 'Animal is running'
class Dog(Animal):
"""定义一个子类, 名为Dog, 该类继承了Aniaml父类"""
pass
class Cat(Animal):
"""定义一个子类, 名为Cat, 该类继承了Aniaml父类"""
pass
dog = Dog()
dog.run() // 输出结果 Animal is running
cat = Cat()
cat.run() // 输出结果 Animal is running
继承最大的好处是子类获得了父类的全部功能
,由于Aniaml实现了run()方法,因此Dog和Cat作为它的子类,就自动拥有了run()方法。
当子类和父类都存在相同的run()方法时,子类的run()覆盖了父类的run(),再代码运行的时候,总是会调用子类的run()
。这样,我们就获得了继承的另一个好处,多态。
class Animal(object):
"""定义一个基类(父类或超类)"""
def run(self):
"""基类有一个run方法"""
print 'Animal is running'
class Dog(Animal):
"""定义一个子类, 名为Dog"""
def run(self):
"""Dog类拥有run方法"""
print 'Dog is running'
class Cat(Animal):
"""定义一个子类, 名为Cat"""
def run(self):
"""Cat类拥有run方法"""
print 'Cat is running'
dog = Dog()
dog.run() // 输出结果:Dog is running
cat = Cat()
cat.run() // 输出结果:Cat is running
继承可以把父类的所有功能都直接拿过来,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。
有了继承,才能有多态
旧的方式定义Python类允许不从object类继承,但这种编程方式已经严重不推荐使用。任何时候,如果没有合适的类可以继承,就继承自object类。
当父类中的方法不符合我们需求时,可以通过重写,实现我们的需求。
class Animal(object):
"""创建一个基类"""
def __init__(self):
self.name = '所有动物的名字'
def eat(self):
print '所有的动物都具有eat的方法'
class Dog(Animal):
"""创建一个子类"""
def eat(self):
print '子类也具有eat的方法'
dog = Dog()
dog.eat() # 执行结果:子类也具有eat的方法; 默认调用子类的方法, 将父类的eat方法进行了覆盖
调用父类的方法,详见代码下面代码注释:
class Animal(object):
"""创建一个基类"""
def __init__(self):
self.name = '所有动物的名字'
def eat(self):
print '所有的动物都具有eat的方法'
class Dog(Animal):
"""创建一个子类"""
def eat(self):
# 方法1:再重写的方法内强制调用 父类名.父类方法名() 若有参数, 则需传参
# Animal.eat(self)
# 方法2: super(子类名, 子类对象).父类方法() 若有参数, 则需传参
# super(Dog, self).eat()
# 方法3:仅支持Python3
# super().eat()
pass
dog = Dog()
dog.eat() # 执行结果:所有的动物都具有eat的方法
class Animal(object):
"""创建一个基类"""
def __init__(self):
self.name = '所有动物的名字'
def eat(self):
print '所有的动物都具有eat的方法'
class Dog(Animal):
"""创建一个子类"""
def __init__(self):
# 方法1: 在重写的方法内强制调用 父类名.父类方法名() 如果需要传参, 则传参
# Animal.__init__(self)
# 方法2: super(子类名, 子类对象).父类方法() 如果需要传参, 则传参
super(Dog, self).__init__()
# 方法3: 仅支持Python3
# super().__init__()
def eat(self):
print '子类的动物都具有eat的方法'
dog = Dog()
print dog.name # 执行结果: 所有动物的名字 调用父类的属性
1.__ doc __:
说明性文档和信息。Python自建,无需自定义。
class Dog(object):
"""描述类的定义信息"""
def eat(self):
pass
# 打印类的说明文档
print(Dog.__doc__) // 输出结果:描述类的定义信息
2.__ init __ 和 __ new __:
class Dog(object):
def __init__(self, name):
self.name = name
self.age = 18
print '类创建实例时, 自动触发执行'
# 在类创建实例的时候, 触发__init__方法
dog_obj = Dog('哈士奇') // 执行结果: 类创建实例时, 自动触发执行
以上是__ init __ 最普通的用法了, 但 __ init __ 其实不是实例化一个类的时候第一个被调用的方法。当使用Dog('哈士奇') 这样的表达式来实例化一个类时, 最先被调用的方法其实是 __ new __ 方法
。
__ new __ 方法接收的参数虽然跟 __ init __ 一样, 但 __ init __ 是在类实例创建之后调用,而 __ new __ 方法正式创建这个类实例的方法。
class Dog(object):
def __new__(cls, name, age):
print '__new__ 方法'
return super(Dog, cls).__new__(cls, name, age)
def __init__(self, name, age):
print '__init__ 方法'
self.name = name
self.age = age
def __str__(self):
return '%s今年已经%s' % (self.name, self.age)
dog = Dog('哈士奇', 18)
print dog
# 执行结果: __new__ 方法 __init__ 方法 哈士奇今年已经18
可以看到, __ new __ 方法的调用是发生在 __ init __ 之前的,其实实例化一个类的时候,具体执行逻辑:
1.dog = Dog(‘哈士奇’, 18)。
2.首先执行name 和 age 参数来执行Dog类的 __ new __ 方法,这个 __ new __ 方法会返回Dog类的一个实例(一般使用super(Dog, cls). __ new __(cls, name, age)这样的方式)。
3.用这个实例来调用类的 __ init __ 方法,上一步里面 __ new __ 产生的实例也就是 __ init __ 里面的 self。
__ init __ 和 __ new __ 方法主要区别:
简单理解,它是实例级别的方法
简单理解,它是类级别的方法
用 __ new __ 来实现单例:
因为类每一次实例后产生的过程都是通过 __ new __ 来控制的,所以通过重载 __ new __ 方法,我们可以实现单例模式。
单例模式:该模式的主要目的是确保某一个类只有一个实例存在
。就是无论再代码中创建多少个实例对象,但是创建出的对象共同指向内存中的同一个地址
。
下面用 __ new __ 方法实现单例模式:
class Dog(object):
instance = None # 这个值就是将要创建的单例对象
# 这三参数是解释器自动添加的, 这些参数实际上是给 __init__ 方法用
def __new__(cls, *args, **kwargs):
if Dog.instance is None:
Dog.instance = object.__new__(cls) # 调用父类的__new__ 方法来开辟内存
return Dog.instance
def __init__(self, name):
self.name = name
dog1 = Dog('哈士奇')
dog2 = Dog('金毛')
print dog1
print dog2
# 执行结果:
<__main__.Dog object at 0x00000000032306D8>
<__main__.Dog object at 0x00000000032306D8>
3.__ module __:
表示当前操作的对象在属于哪个模块。
class Dog(object):
def eat(self):
pass
dog = Dog()
print(dog.__module__) // 输出结果:__main__ 表示的是当前执行模块中
4.__ class __:
class Dog(object):
def eat(self):
pass
dog = Dog()
print(dog.__class__) // 输出结果: 表示当前执行模块中的Dog类
5.__ del __:
析构方法,当对象在内存中被释放时,自动触发此方法。
注意:
class Dog(object):
def __del__(self):
print('马上要被回收了!')
dog = Dog()
del dog // 输出结果:马上要被回收了!
6.__ call __:
如果为一个类编写了该方法,那么在该类的实例后面加括号
,可会调用这个方法。
对于__ call __方法,是由对象后加括号触发的,对象()或类()()
。
class Dog(object):
def __init__(self):
print('要执行init方法了!')
def __call__(self, *args, **kwargs):
print('要执行call方法了!')
# 对象 = 类名()
dog = Dog() # 执行__init__ 方法; 输出结果:要执行init方法了!
# 对象()
dog() # 执行__call__ 方法; 输出结果:要执行call方法了!
# 类()()
Dog()()# 先执行__init__方法,在执行__call__ 方法;输出结果:要执行init方法了!要执行call方法了!
可以用Python内建的callable()函数进行测试,判断一个对象是否可以被执行
。
class Dog(object):
def __call__(self, *args, **kwargs):
print('要执行call方法了!')
print callable(Dog()) // 输出结果: True
7.__ dict __:
列出类或对象中的所有成员。非常重要和有用的一个属性,Python自建,无需用户自己定义。
class Dog(object):
desc = '创建了一个名为Dog的类'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print '它可以吃的方法'
# 获取类的成员
print(Dog.__dict__)
// 输出结果:{'__module__': '__main__', '__doc__': None, '__dict__': , '__weakref__': , 'eat': , '__init__': , 'desc':创建了一个名为Dog的类'}
# 获取 对象dog的成员
dog = Dog('哈士奇', 18)
print(dog.__dict__)
// 输出结果:{'age': 18, 'name': '哈士奇'}
8.__ str __:
如果一个类中定义了str()方法,那么在打印对象时,默认输出该方法的返回值。
class Dog(object):
def __str__(self):
return '在打印对象时, 默认输出该方法的返回值'
dog = Dog()
print(dog)
// 输出结果:在打印对象时, 默认输出该方法的返回值
9.__ getitem __ 、__ setitem __ 、__ delitem __:
在标识符后面加中括号[],通常代表取值的意思。它们分别表示,取值、赋值、删除数据。
obj = 标识符[] // 执行__getitem__ 方法
标识符[] = obj // 执行__setitem__ 方法
del 标识符[] // 执行__delitem__ 方法
class Dog(object):
def __getitem__(self, item):
"""取值"""
print('__getitem__', item)
def __setitem__(self, key, value):
"""赋值"""
print('__setitem__', key, value)
def __delitem__(self, key):
print('__delitem__', key)
dog = Dog()
result = dog['哈士奇'] # 自动触发执行__getitem__ 输出结果:('__getitem__', '哈士奇')
dog['哈士奇'] = '16' # 自动触发执行__setitem__ 输出结果: ('__setitem__', '哈士奇', '16')
del dog['哈士奇'] # 自动触发执行 __delitem__ 输出结果:('__delitem__', '哈士奇')
10.__ iter __:
迭代器方法,列表、字段、元组之所以可以进行for循环,是因为内部定义了__ iter __ 这个方法。
若想让自定义的类的对象可以被迭代,那么就需要在类中定义这个方法,并让该方法的返回值是一个可迭代的对象。当在代码中使用for循环遍历对象时,就会调用类的这个__ iter __方法
。
// 下面举一个常见的错误栗子
class Dog(object):
pass
dog = Dog()
for obj in dog:
print obj
# 报错信息:TypeError: 'Dog' object is not iterable
# 原因: dog对象 不可以被迭代
# 下面添加一个__iter__(), 但是什么都不返回
class Dog(object):
def __iter__(self):
pass
dog = Dog()
for obj in dog:
print obj
# 报错信息:TypeError: iter() returned non-iterator of type 'NoneType'
# 原因是__iter__ 方法没有返回一个可迭代的对象
// 下面举一个正确的栗子:
class Dog(object):
def __init__(self, my_list):
self.my_list = my_list
def __iter__(self):
return iter(self.my_list) // 返回一个iter()可迭代对象
dog = Dog([10, 20, 30, 40, 50])
for item in dog:
print item
# 输出结果:
10
20
30
40
50
11.__ len __:
返回元素的个数。
class Dog(object):
def __init__(self, *args):
self.name = args
def __len__(self):
return len(self.name)
dog = Dog('哈士奇', '金毛')
print len(dog) // 输出结果:2
12.__ repr __:
这个方法的作用和__ str __ 很像,两者的区别是:__ str __ 返回用户看到的字符串,而 __repr __ 返回程序开发者看到的字符串,简单说 __ repr __ 是为了调试服务的
。
下面说说二者的区别吧:
In [2]: class Dog(object):
...: def __init__(self, name):
...: self.name = name
...:
...: def __str__(self):
...: return 'this is %s' % self.name
...:
In [3]: dog = Dog("哈士奇")
In [4]: dog
Out[4]: <__main__.Dog at 0x5062518>
In [5]: class Dog(object):
...: def __init__(self, name):
...: self.name = name
...: def __repr__(self):
...: return 'this is %s' % self.name
...:
In [6]: dog = Dog('哈士奇')
In [7]: dog
Out[7]: this is 哈士奇
13.__ add __ :加运算、 __ sub __ :减运算、__ mul __ :乘运算、 __ div __ :除运算、__ mod __ :求余运算、 __ pow __ :幂运算:
这些都是算术运算方法。
class Number(object):
def __init__(self, numner):
self.numner = numner
def __add__(self, other):
print '使用加号的时候, 执行__add__方法'
return self.numner + other
def __sub__(self, other):
print '使用减号的时候, 执行__sub__方法'
return self.numner - other
def __mul__(self, other):
print '使用乘号的时候, 执行__mul__方法'
return self.numner * other
number = Number(6)
print number + 2 # 输出结果: 调用加号的时候, 执行__add__方法 7
print number - 2 # 输出结果: 使用减号的时候, 执行__sub__方法 3
print number * 2 # 输出结果: 使用乘号的时候, 执行__mul__方法 10
14.__ slots __:
Python作为一种动态语言,可以在类定义完成和实例化后,给类或者对象继续添加随意个数或者任意类型的变量或方法,这是动态语言的特性。
正常情况下,当我们定义一个class, 创建了一个class实例后,我们可以给该实例绑定任何属性和方法。但是,如果我们想要限制实例的属性怎么办?这时我们可以在定义class的到时候,定义一个特殊的__ slots __ 变量,来限制该class实例能添加的属性
。
class Dog(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print '{}它今年{}'.format(self.name, self.age)
dog = Dog('金毛', 19)
dog.eat() # 打印结果: 金毛它今年19
dog.name = '哈士奇'
dog.age = 18
dog.eat() # 打印结果: 哈士奇它今年18 说明给属性赋值成功
dog.sex = '公' # 报错信息:AttributeError: 'Dog' object has no attribute 'sex'
# 原因: 由于'sex' 没有被放到__slots__中,所以不能绑定sex属性
注意:使用__ slots __ 定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。
class Dog(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print '{}它今年{}'.format(self.name, self.age)
class HaShiQi(Dog):
"""创建一个哈士奇的子类, 继承Dog类"""
pass
hashiqi = HaShiQi('哈士奇', 20)
# 可以子类可以使用'sex'属性
hashiqi.sex = '男'
除非在子类中也定义__ slots __ ,子类实例允许定义的属性就是自身的 __ slots __ 加上父类的 __ slots __。
class Dog(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print '{}它今年{}'.format(self.name, self.age)
class HaShiQi(Dog):
"""创建一个哈士奇的子类, 继承Dog类"""
__slots__ = ('sex')
hashiqi = HaShiQi('哈士奇', 20)
# 如果子类设置了__slots__, 那么子类的对象可以调用父类的 __slots__允许属性
hashiqi.name = '哈士奇的名字'
# 如果子类设置了__slots__, 那么子类的或父类中的__slots__ 都没有该属性的话,
# 直接报错: AttributeError: 'HaShiQi' object has no attribute 'nickname'
hashiqi.nickname = '哈士奇的昵称'
如果想让内部属性不被外部访问,可以把属性的名称前加上两个下划线__, 在Python中,实例的变量如果以双下划线开头,就变成了一个私有变量,只有内部才可以访问,外部不能访问
。
class Dog(object):
def __init__(self, name, age):
self.__name = name
self.__age = age
def eat(self):
print('%s今年%s' % self.__name, self.__age)
dog = Dog('哈士奇', 20)
dog.__name # 报错信息:AttributeError: 'Dog' object has no attribute '__name'
// 对于外部代码来说,没有什么变动,但是已经无法从外部访问
使用get_set_del方法操作私有成员:
class Dog(object):
def __init__(self, name):
self.name = name
def eat(self):
print("%s在吃东西" % self.name)
# 加上双下划线的就是私有变量, 只能在类的内部访问, 外部无法访问
__age = 18
# 如果在类中调用, 首先调用类方法
@classmethod
def method(cls):
print(cls.__age)
# 在调用set_ 方法, 改变__age的值
@classmethod
def set_age(cls, value):
cls.__age = value
return cls.__age
# 调用get_ 方法, 直接返回__age的值
@classmethod
def get_age(cls):
return cls.__age
# 调用del_ 方法, 直接删除__age的值
@classmethod
def del_age(cls):
del cls.__age
print Dog.get_age() # 执行结果:18 因为调用的是__age 私有属性
print Dog.set_age(20) # 执行结果:20 因为直接改变了私有属性__age 的值
print Dog.del_age() # 执行结果:None 因为直接删除了__age的值
双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__ age 是因为Python解释器对外把__ age 变成了 _Dog __ age
, 所以仍然可以通过 _ Dog __ age来访问 __ age变量。
print(dog._Dog__age) // 对象._类名__私有属性
把类的方法伪装成属性调用的方式,就是把类里面的一个函数,变成一个属性
。
下面举个常规操作的栗子:
class Dog(object):
def __init__(self, name, age):
self.__name = name
self.__age = age
def get_age(self):
return self.__age
def set_age(self, value):
if isinstance(value, int):
self.__age = value
else:
raise ValueError('非整数类型')
def del_age(self):
print '删除该操作'
dog = Dog('哈士奇', 18)
print dog.get_age() # 输出结果:18
dog.set_age("20") # 报错:ValueError: 非整数类型
dog.set_age(20)
print dog.get_age() # 输出结果:20 已经将传入value进行了赋值__age
下面举个Property装饰器栗子:
class Dog(object):
def __init__(self, name, age):
self.__name = name
self.__age = age
# 将age()函数, 更改了属性
@property
def age(self):
print '将age方法变成属性'
return self.__age
@age.setter
def age(self, value):
print '调用了setter方法'
if isinstance(value, int):
self.__age = value
else:
raise ValueError('非整数类型')
@age.deleter
def age(self):
print '删除了__age'
# 创建对象
dog = Dog('哈士奇', 18)
# 通过@prepety装饰器, 将age()方法, 变成了一个属性, 可以通过对象.属性 就可以获取
print dog.age # 输出结果:将age方法变成属性 18
# 使用setter把值进行转换
dog.age = 20
print dog.age # 输出结果:调用了setter方法;将age方法变成属性;20
# 删除age
del dog.age # 输出结果:删除了__age
下面举个更加简单直观的使用property()函数
栗子:
Python内置的builtins模块中的property()函数,提供了第二种设置类属性的手段
。class Dog(object):
def __init__(self, name, age):
self.__name = name
self.__age = age
def get_age(self):
return self.__age
def set_age(self, age):
print '调用了赋值的方法'
if isinstance(age, int):
self.__age = age
else:
raise ValueError('非整数类型')
def del_age(self):
print '删除__age'
age = property(get_age, set_age, del_age, '年龄')
dog = Dog('哈士奇', 18)
print dog.age # 输出结果: 18
dog.age = 20 # 输出结果: 调用了赋值的方法; 20
print dog.age # 输出结果: 20
del dog.age # 输出结果: 删除了__age
通过语句age = property(get_age, set_age, del_age, ‘年龄’) 将一个方法伪装成了属性,效果和装饰器的方法是一样的。
property()函数的参数
:
第一个参数是方法名, 调用 实例.属性 时自动执行的方法
第二个参数是方法名, 调用 实例.属性 = xxx 时自动执行的方法
第三个参数是方法名, 调用 del 实例.属性 时自动执行的方法
第四个参数时字符串, 调用 实例.属性.__doc__ 时的描述信息