那年夏天

Python3 面向对象

2019-01-11  本文已影响0人  坚持到底v2

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. 函数

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 专有方法:

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 创建出来的“实例”。

待续

上一篇 下一篇

猜你喜欢

热点阅读