60D2 – 数据类型

基本数据类型

自定义类型

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 入门者,可以暂时不用太纠结这部分理论,使用一段时间后自然会理解。

页面: 1 2 3 4

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注