1.访问可见性问题
- 对象属性的访问权限包括私有的、受保护的和公开的,而对象的方法通常是公开的。如果希望属性是私有的,即不允许外界访问,在给属性命名时可以用两个下划线作为开头,如:
class Car(object):
def __init__(self, model="BMW", year=2019, foo='abc'):
self.model = model
self.year = year
self.__foo = foo
>>> car = Car()
>>> car.model
BMW
>>> car.__foo #无法从外界获取私有属性
AttributeError: 'Car' object has no attribute '__foo'
2.类可以截获Python运算符
用类写成的对象可以截获并响应应用在内置类型上的运算:加法、切片、打印和点号运算等,这也称为运算符重载,以下是运算符重载的概要:
- 以双下划线命名的方法__X__构造,python语言替每种运算和特殊命名的方法之间定义了固定不变的映射关系, __init__是最常用的运算符重载方法
- 当实例出现内置运算时,这类方法会自动调用。如实例对象继承了__add__方法,当对象出现在 + 表达式内时,该方法就会调用
- 类可覆盖多数内置类型运算
- 运算符覆盖方法没有默认值,而且也不需要。如果类没有定义或继承运算符重载方法,则相应的运算在类实例中并不支持,如果没有__add__,+表达式就会引发异常
class Firstclass(object):
def __init__(self, data):
self.data = data
def __add__(self, other):
return Firstclass(self.data + other + '!')
def __str__(self):
return 'Firstclass: %s' % self.data
a = Firstclass('abc')
print(a)
b = a + 'xyz'
print(b)
>>> Firstclass: abc
>>> Firstclass: abcxyz!
3.property 装饰器
一般我们不建议把属性设置为私有的,但如果直接将属性暴露给外界也会有问题,如没有办法检查赋给属性的值是否有效,因此建议将属性以单下划线开头,来暗示属性是受保护的,不建议外界直接访问,那么如果想访问属性可以通过属性的 getter(访问器)和setter(修改器)方法进行对应的操作。如果要做到这点,就可以考虑使用@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便,如:
class Person(object):
def __init__(self, name, age):
self._name = name
self._age = age
# 访问器 -getter方法
@property
def name(self):
return self._name
# 访问器 -getter方法
@property
def age(self):
return self._age
# 修改器 - setter方法
@age.setter
def age(self, age):
self._age = age
def play(self):
if self._age <= 18:
print('%s正在喝可乐.' % self._name)
else:
print('%s正在喝酒.' % self._name)
person = Person('Randoll', 22)
person.play()
print(person.age)
person.age = 16
person.play()
print(person.age)
>>> Randoll正在喝酒.
>>> 22
>>> Randoll正在喝可乐.
>>> 16
3.__slots__魔法
通过定义__slots__变量可以限定自定义类型的对象只能绑定某些属性,但只对当前类生效,对子类无效
class Person(object):
# 限定Person对象只能绑定_name, _age, _number属性
__slots__ = ('_name', '_age', '_number')
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
def play(self):
if self._age <= 18:
print('%s正在喝可乐.' % self._name)
else:
print('%s正在喝酒.' % self._name)
person = Person('王大锤', 22)
person.play()
person._number = 123456
person._gender = 'male'
>>> AttributeError: 'Person' object has no attribute '_gender'
4.静态方法和类方法
之前在类中定义的都是对象方法,都是发送给对象的消息;但类中的方法除了对象方法,也可以是静态方法,如:
from math import sqrt
#定义一个"三角形"类
class Triangle(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
#验证是否能构成三角形
@staticmethod
def is_valid(a, b, c):
return a + b > c and b + c > a and a + c > b
#计算三角形周长的方法
def perimeter(self):
return self._a + self._b + self._c
a, b, c = 3, 4, 7
# 静态方法和类方法都是通过给类发消息来调用的
if Triangle.is_valid(a, b, c): #在构成对象前调用类的静态方法
t = Triangle(a, b, c)
print(t.perimeter())
else:
print('无法构成三角形.')
>>> 无法构成三角形.
和静态方法比较类似,Python还可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,如:
from time import time, localtime, sleep
class Clock(object):
"""数字时钟"""
def __init__(self, hour=0, minute=0, second=0):
self._hour = hour
self._minute = minute
self._second = second
@classmethod
def now(cls):
ctime = localtime(time())
return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)
def show(self):
"""显示时间"""
return '%02d:%02d:%02d' % \
(self._hour, self._minute, self._second)
# 通过类方法创建对象并获取系统时间
clock = Clock.now()
clock.show()
>>> '15:11:47'
5.查看类或者对象的属性
>>> Firstclass.__bases__ #查看当前类的超类
(object,)
>>> Firstclass.__dict__.keys() #查看当前类的属性
dict_keys(['__module__', '__init__', '__add__', '__str__', 'mul', '__dict__', '__weakref__', '__doc__'])
>>> a.__dict__.keys() #查看对象的属性
dict_keys(['data'])
>>> a.__class__ #查看实例关联的类
__main__.Firstclass