Python 面向对象

类变量的访问限制

class Data:
    def __init__(self, arg1, arg2):
        self.__arg1 = arg1
        self.__arg2 = arg2
    def print_args(self):
        print(f"arg1:{self.__arg1}, arg2:{self.__arg2}")

data = Data("1", 2)
data.print_args() # arg1:1, arg2:2
print(data.__arg1) # 报错 'Data' object has no attribute 'arg1'
print(data.__arg2) # 报错 'Data' object has no attribute 'arg2'

当变量名为 __arg1 这样的形式时,类的示例就不能直接访问。
因为 Python 解释器将 __arg1 改成了 _Data__arg1

data = Data("1", 2)
print(data._Data__arg1) # 1

获取对象的信息

对象的类型

type(data) # __main__.Data

比对对象类型

isinstance(data, Data) # True

判断是否是函数

import types
def my_func():
    ... # 三个点相当于 pass

type(my_func) == types.FunctionType # True
type(max) == types.BuiltinFunctionType # True
type(lambda x: x*x) == types.LambdaType # True
type((x for x in [1, 2, 3])) == types.GeneratorType # True

获取对象的所有属性和方法

dir(data)
"""
['_Data__arg1',
 '_Data__arg2',
 '__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'print_args']
"""

demo_str = "demo_str"
dir(demo_str)
"""
['__add__',
 '__class__',
 '__contains__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__getnewargs__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__mod__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rmod__',
 '__rmul__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'capitalize',
 'casefold',
 'center',
 'count',
 'encode',
 'endswith',
 'expandtabs',
 'find',
 'format',
 'format_map',
 'index',
 'isalnum',
 'isalpha',
 'isascii',
 'isdecimal',
 'isdigit',
 'isidentifier',
 'islower',
 'isnumeric',
 'isprintable',
 'isspace',
 'istitle',
 'isupper',
 'join',
 'ljust',
 'lower',
 'lstrip',
 'maketrans',
 'partition',
 'replace',
 'rfind',
 'rindex',
 'rjust',
 'rpartition',
 'rsplit',
 'rstrip',
 'split',
 'splitlines',
 'startswith',
 'strip',
 'swapcase',
 'title',
 'translate',
 'upper',
 'zfill']
"""

操作属性

获取属性

getattr()

getattr(data, "_Data__arg1") # 1

设置属性

setattr()

setattr(data, "name", "ok")
print(data.name) # ok

判断是否有属性

hasattr()

hasattr(data, "_Data__arg1") # True

实例属性和类属性

实例属性

class Data:
    def __init__(self):
        self.name = "name"
data = Data()
print(data.name) # name

限制实例能添加的属性

class Data:
    __slots__ = ("name", "age") # 用元组定义允许添加的属性名称

data = Data()
data.name = "name" # 不报错
data.num = "num" # AttributeError: 'Data' object has no attribute 'num'

class Data2(Data):
    pass

data2 = Data2()
data2.num = "num" # 不报错

__slots__ 仅对当前类有效,对子类无效。

类属性

class Data:
    name = "name"
    def __init(self):
        pass
data = Data()
print(data.name) # name

实例方法

给单个实例绑定方法

class Data:
    def __init__(self):
        pass

from types import MethodType

def get_name(self): # 注意这里要加 self
    return "name"
data = Data()
data.get_name = MethodType(get_name, data)
print(data.get_name()) # name
data2 = Data()
print(data2.get_name()) # AttributeError: 'Data' object has no attribute 'get_name'

这个方法仅对当前示例有效。

给所有实例绑定方法

class Data:
    def __init__(self):
        pass

def get_name(self): # 注意这里要加 self
    return "name"

Data.get_name = get_name

data = Data()
print(data.get_name()) # name
data2 = Data()
print(data2.get_name()) # name

将方法当作属性使用

class Data:
    def __init__(self):
        pass
    @property
    def num(self):
        if not hasattr(self, "_num"):
            self._num = "None" # 这里注意不要写成 self.num,否则会死循环
        return self._num
    @num.setter
    def num(self, new_num):
        self._num = new_num
data = Data()
print(data.num) # None
data.num = "new_num"
print(data.num) # new_num

子类拓展父类

class Father(object):
    def get_args(self, *args):
        father_args = ["father arg1", "father arg2"]
        father_args += args
        return father_args


class Son(Father):
    def get_args(self, *args):
        return super(Son, self).get_args(*args)

son = Son()
son_args = ["son arg1", "son arg2"]
demo = son.get_args(*son_args)
print(demo)

# out
['father arg1', 'father arg2', 'son arg1', 'son arg2']

Python 2 和 3 中 super() 的区别

class A:
     def add(self, x):
         y = x+1
         print(y)
class B(A):
    def add(self, x):
        super().add(x)
b = B()
b.add(2)  # 3

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class A(object):   # Python2.x 记得继承 object
    def add(self, x):
         y = x+1
         print(y)
class B(A):
    def add(self, x):
        super(B, self).add(x)
b = B()
b.add(2)  # 3

定制类

类中定义了 __len__() 方法,就可以用 len()

class Data:
    def __init__(self):
        pass
    def __len__(self):
        return 100
data = Data()
print(len(data)) # 100

类中定义了 __str__() 方法,就可以用 str()

class Data:
    def __init__(self):
        pass
    def __str__(self):
        return f"I'm Data"
    __repr__ = __str__ # print() 调用 __str__,ipython 交互解释器里直接输出调用 __repr__,这里让他俩输出一样
data = Data()
print(str(data)) # I'm Data

类中定义了 __iter__() 方法,就可以被迭代`。

class Data:
    def __init__(self):
        self.num = 0
    def __iter__(self):
        return self
    def __next__(self):
        self.num += 1
        if self.num > 10:
            raise StopIteration()
        return self.num

for num in Data():
    print(num)
"""
1
2
3
4
5
6
7
8
9
10
"""

类中实现了 __getitem__() 方法,可以按照下标取值。

class Data:
    def __getitem__(self, n): # n 可以是下标也可以是切片
        nums = [1, 2, 3, 4, 5, 6]
        if isinstance(n, int):
            for index, value in enumerate(nums):
                if index == n:
                    return value
        elif isinstance(n, slice):
            start = n.start
            stop = n.stop
            return nums[n.start:n.stop]
data = Data()
print(data[3]) # 4
print(data[2:4]) # [3, 4]

类中实现了 __getattr__ 可以动态设置属性,以下是一个将链式调用转为路径的例子。

class Data:
    def __init__(self, path=""):
        self._path = path
    def __getattr__(self, path):
        return Data(f"{self._path}/{path}")
    def __str__(self):
        return self._path
    __repr__ = __str__
print(Data().user.name.age.nums) # /user/name/age/nums

类中实现了 __call__,他的实例就可以直接在后面加 () 调用。

class Data:
    def __call__(self):
        print("calling")
data = Data()
data()

判断变量是否可调用

callable(data) # True

枚举类

如果要枚举出十二生肖,除了直接定义常量,更好的方法是用枚举类:

from enum import Enum

Zodiac = Enum(
    "Zodiac",
    (
        "rat",
        "cattle",
        "tiger",
        "rabbit",
        "dragon",
        "snack",
        "horse",
        "sheep",
        "monkey",
        "chicken",
        "dog",
        "pig"
    )
)

for name, member in Zodiac.__members__.items():
    print(
        f"name:{name}\tmember:{member}\tindex:{member.value}" # member.value 是自动加上的,默认从1开始
    )

"""
name:rat        member:Zodiac.rat       index:1
name:cattle     member:Zodiac.cattle    index:2
name:tiger      member:Zodiac.tiger     index:3
name:rabbit     member:Zodiac.rabbit    index:4
name:dragon     member:Zodiac.dragon    index:5
name:snack      member:Zodiac.snack     index:6
name:horse      member:Zodiac.horse     index:7
name:sheep      member:Zodiac.sheep     index:8
name:monkey     member:Zodiac.monkey    index:9
name:chicken    member:Zodiac.chicken   index:10
name:dog        member:Zodiac.dog       index:11
name:pig        member:Zodiac.pig       index:12
"""

自定义枚举类

from enum import Enum, unique

@unique # 保证没有重复值
class Zodiac(Enum):
    rat = 1
    cattle = 2
    tiger = 3
    rabbit = 4
    dragon = 5
    snack = 6
    horse = 7
    sheep = 8
    monkey = 9
    chicken = 10
    dog = 11
    pig = 12

zod = 1
print(zod == Zodiac.rat.value) # True

(本文完)

原文地址:https://www.cnblogs.com/junsircoding/p/15664982.html