1. 简单示例
class Student(object):
"""
一个简单的类实例
"""
def __init__(self, name, score):
super(Student,self)
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
# 实例化类
x2 = Student('Bart Simpson', 59)
x2.print_score()
# 输出 Bart Simpson: 59
2. 函数
-
类的实例方法与普通的函数有一个特别的区别--它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self , 表示对象自身
-
类还有静态方法和类方法,分别使用 @staticmethod 和 @classmethod 修饰,
- 对于静态方法,参数没有要求,
- 对于类方法,第一个参数一般使用 cls 表示是类本身
2.1 例子:
class MyClass:
@classmethod
def f1(cls):
return str(cls.y)
def f2(self):
return str(self.y)
x = MyClass()
MyClass.y=1
x.y=12
# 调用 类方法f1 输出 1
print(MyClass.f1()=='1')
print(x.f1()==MyClass.f1())
# 调用实例方法f2 输出 12
print(x.f2()=='12')
# 类直接调用 实例方法会报异常
try:
MyClass.f2()
except:
print("MyClass 调用 实例方法f2 报错")
3. 私有属性和方法
使用 __
开头的属性和方法 不能被子类继承
其实是因为Python解释器对外把
__name
变量改成了_ClassName__name
,
但是强烈建议你不要基于这个机制去访问私有属性,因为不同版本的Python解释器可能会把__name
改成不同的变量名。
总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉.
另外不要直接设置类似于__name
这样的属性(因为类可以随意扩展属性), 因为前面说了 Python解释器 已经把__name
改成了_ClassName__name
, 你这样做相当于是扩展了一个新的__name
属性而已, 并不是修改的私有属性__name
(也就是_ClassName__name
)
3.1 专有方法:
__init__
-
__del__
: 析构函数 -
__repr__
: 打印,转换 -
__setitem__
: 按照索引赋值 -
__getitem__
: 按照索引获取值 -
__len__
: 获得长度 -
__cmp__
: 比较运算 -
__call__
: 函数调用 -
__add__
: 加运算 -
__sub__
: 减运算 -
__mul__
: 乘运算 -
__div__
: 除运算 -
__mod__
: 求余运算 -
__pow__
: 乘方
4. 继承
class DerivedClassName(BaseClassName1,BaseClassName2):
pass
如果基类中有相同的方法名,而在子类使用时未指定,python从左至右搜索
即方法在子类中未找到时,按从左到右的顺序查找基类中是否包含方法
5. 探索对象 dir()
如果要获得一个对象的所有属性和方法,可以使用 dir()
函数,
它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:
print(dir('ABC'))
# 输出 ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
print(len('ABC')) # 等价于 'ABC'.__len__()
仅仅把属性和方法列出来是不够的,配合 getattr()
、 setattr()
以及 hasattr()
,我们可以直接操作一个对象的状态:
hasattr(obj, 'x') # 有属性'x'吗?
# True
setattr(obj, 'y', 19) # 设置一个属性'y'
getattr(obj, 'y') # 获取属性'y'
# 19
getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404 , 而不是抛出异常
# 404
当探索一个类实例的某个对象的属性时, 如果该对象没有此属性, 则会使用其所属类的类属性, 并可能应该会按照这个逻辑一直递归到顶层.
class Student(object):
# name 属性是属于Student类共有的
name="Student"
s = Student()
print(s.name) # 此时 s对象没有 name 属性, 所以使用 Student 类的属性
s.name='myName' # 设置后 s对象由了 name 属性
del s.name # 删除 s对象的name属性
6. 使用 __slots__
正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
class Student(object):
pass
def set_age(self, age): # 定义一个函数作为实例方法
self.age = age
s = Student()
from types import MethodType
s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
s.set_age(25) # 调用实例方法
print(s.age) # 测试结果
s2 = Student()
Student.set_age = set_age # 可以给class绑定方法 后,所有该类的对象都可以使用set_age
s2.set_age(25) # 调用实例方法
print(s2.age) # 测试结果
但是,如果我们想要限制实例的属性怎么办?
比如,只允许对Student实例添加name和age属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的 __slots__
变量,来限制该class实例能添加的属性:
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
使用 __slots__
要注意, __slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的, 这也是显而易见的,
除非在子类中也定义 __slots__
,这样,子类实例允许定义的属性就是自身的 __slots__
加上父类的 __slots__
7 使用@property
class Student(object):
_score=None
@property
def score(self):
return self._score
@score.setter
def score(self, value):
# some code
self._score = value
s=Student()
s.score=100
print(s.score)
8. 使用枚举类
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
# 这样我们就获得了Month类型的枚举类,可以直接使用 Month.Jan 来引用一个常量,或者枚举它的所有成员:
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
# 输出
"""
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12
"""
value属性则是自动赋给成员的int常量,默认从1开始计数。
如果需要更精确地控制枚举类型,可以从Enum派生出自定义类:
from enum import Enum, unique
# @unique装饰器可以帮助我们检查保证没有重复值。
@unique
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
9. 使用元类
9.1 type
type()
不仅可以返回一个对象的类型,还可以创建出新的类型.
正常情况下,我们都用 class Xxx...
来定义类,但是,type()函数
也允许我们动态创建出类来,
也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,
要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。
示例
def fn(self, name='world'): # 先定义函数
print('Hello, %s.' % name)
# 使用 type 函数创建 Hello class
# 第一个参数是 类名称
# 第二个参数是继承的父类集合
# 第三个参数是class的方法名称与函数绑定,这里我们把 函数fn 绑定到方法名 hello上
Hello = type('Hello', (object,), dict(hello=fn))
h = Hello()
h.hello()
# 输出 Hello, world.
9.2 metaclass
metaclass
允许你创建类或者修改类。
换句话说,你可以把类看成是 metaclass
创建出来的“实例”。
待续
网友评论