基本数据类型
自定义类型
Python 使用关键字 class 定制自己的类,self 表示类实例对象的本身。
一个自定义类内包括属性、方法,其中有些方法是自带的。
类(对象)
class Dog(object):
pass
这定义了一个 Dog 对象,它继承于根类 object,pass 表示没有自定义任何属性和方法。
下面创建一个 Dog 类型的实例:
wangwang = Dog()
Dog 类现在没有定义任何方法,但是刚才说了,它会有自带的方法,使用 __dir__() 查看这些自带方法:
In [26]: wangwang.__dir__()
Out [26]:
['__module__',
'__dict__',
'__weakref__',
'__doc__',
'__repr__',
'__hash__',
'__str__',
'__getattribute__',
'__setattr__',
'__delattr__',
'__lt__',
'__le__',
'__eq__',
'__ne__',
'__gt__',
'__ge__',
'__init__',
'__new__',
'__reduce_ex__',
'__reduce__',
'__subclasshook__',
'__init_subclass__',
'__format__',
'__sizeof__',
'__dir__',
'__class__']
有些地方称之为魔法方法,它们与创建类时自定义的个性化行为有关。比如说:
- __init__ 方法能定义一个带参数的类;
- __new__ 方法能自定义实例化类的行为;
- __getattribute__ 方法能自定义读取属性的行为;
- __setattr__ 自定义赋值与修改属性时的行为。
类的属性:
def __init__(self, name, dtype):
self.name = name
self.dtype = dtype
通过 __init__,定义 Dog 对象的两个属性:name 和 dtype。
类的实例:
wangwang = Dog('wangwang', 'cute_type')
wangwang 是 Dog 类的实例。
类的方法:
def shout(self):
print('I\'m %s, type' % (Self.name, self.dtype))
注意:
- 自定义方法的第一个参数必须是 self,它指向实例本身,如 Dog 类型的实例 dog
- 引用属性时,必须前面添加 self,比如 self.name 等。
总结以上代码:
In [40]: class Dog(object):
...: def __init__(self,name,dtype):
...: self.name=name
...: self.dtype=dtype
...: def shout(self):
...: print('I\'m %s, type: %s' % (self.name, self.dtype))
In [41]: wangwang = Dog('wangwang','cute_type')
In [42]: wangwang.name
Out[42]: 'wangwang'
In [43]: wangwang.dtype
Out[43]: 'cute_type'
In [44]: wangwang.shout()
I'm wangwang, type: cute_type
可以看到创建的两个属性和一个方法都被暴露在外面,可被 wangwang 调用。这样的话,这些属性就会被任意修改:
In [49]: wangwang.name='wrong_name'
In [50]: wangwang.name
Out[50]: 'wrong_name'
如果想避免属性 name 被修改,可以将它变为私有变量。
改动方法:属性前加 2 个 _ 后变为私有属性。例如:
In [51]: class Dog(object):
...: def __init__(self,name,dtype):
...: self.__name=name
...: self.__dtype=dtype
...: def shout(self):
...: print('I\'m %s, type: %s' % (self.name, self.dtype))
同理,方法前加 2 个 _ 后,方法会变成“私有方法”,只能在 Dog 类被共享使用。
但是这样改动后,属性 name 不能访问了,也就无法得知 wangwang 的名字叫啥。
不过,这个问题有一种简单的解决方法,直接定义一个新方法即可:
def get_name(self):
return self.__name
综合代码:
In [52]: class Dog(object):
...: def __init__(self,name,dtype):
...: self.__name=name
...: self.__dtype=dtype
...: def shout(self):
...: print('I\'m %s, type: %s' % (self.name, self.dtype))
...: def get_name(self):
...: return self.__name
...:
In [53]: wangwang = Dog('wangwang','cute_type')
In [54]: wangwang.get_name()
Out[54]: 'wangwang'
但是,通过这种机制改变属性的可读性或可写性,看上去不太优雅。因为这在无形中增加了冗余的方法 get_name。
下面通过另一个例子来解释如何优雅地改变某个属性为只读或只写。
自定义一个最精简的 Book 类,它继承于系统的根类 object:
class Book(object):
def __init__(self,name,sale):
self.__name = name
self.__sale = sale
使用 Python 自带的 property 类,就可以优雅地将 name 变为只读:
@property
def name(self):
return self.__name
使用 @property 装饰后 name 变为属性,意味着 .name 就会返回这本书的名字,而不是通过 .name() 这种函数调用的方法。这样变为真正的属性后,可读性更好。
In [101]: class Book(object):
...: def __init__(self,name,sale):
...: self.__name = name
...: self.__sale = sale
...: @property
...: def name(self):
...: return self.__name
In [102]: a_book = Book('magic_book',100000)
In [103]: a_book.name
Out[103]: 'magic_book'
property 是 Python 自带的类,前三个参数都是函数类型。
更加详细的讨论将在后面关于装饰器时再展开。
In [104]: help(property)
Help on class property in module builtins:
class property(object)
| property(fget=None, fset=None, fdel=None, doc=None)
如果要使 name 既可读又可写,那就再加上一个装饰器 @name.setter 。
In [105]: class Book(object):
...: def __init__(self,name,sale):
...: self.__name = name
...: self.__sale = sale
...: @property
...: def name(self):
...: return self.__name
...: @name.setter
...: def name(self,new_name):
...: self.__name = new_name
In [106]: a_book = Book('magic_book',100000)
In [107]: a_book.name = 'magic_book_2.0'
In [108]: a_book.name
Out[108]: 'magic_book_2.0'
注意这种装饰器的写法!
name.setter ,name 已经被包装为 property 实例,调用实例上的 setter 函数再包装 name 后就会可写。
对于 Python 入门者,可以暂时不用太纠结这部分理论,使用一段时间后自然会理解。