Python 之面向对象

  1. 私有属性和方法
  2. 实例属性和类属性
    1. 属性限制 slots
  3. 属性装饰器 @property @classmethod @staticmethod
    1. classmethod 是用来指定一个类的方法为类方法,一般要调用一个类定义的方法需要通过实例化出一个实例来进行调用,但是 classmethod 允许类的某个方法直接被调用,常用与工厂模式。
  4. 多重继承
  5. 类装饰器、属性装饰器
  6. 重载运算符

私有属性和方法

Python没有像C++语言或者Java语言中private 和public的语法来声明私有属性和共有属性。
默认,在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,也就是默认的属性和方法都是共有的。
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线
private

protected _
不能修改继承的私有属性
示例:

class Student:
    name = ""
    __pid = ""
    def __init__(self, name, pid):
        self.name = name
        self.__pid = pid
    def display(self):
        print(f"name: {self.name}\npid: {self.__pid}")


class PresidentStudent(Student):
    def set_pid(self, pid):
        self.__pid = pid

ps.display()
ps.set_pid("123")
# 实际上pid并不对发生变化
ps.display()

实例属性和类属性

Python 是动态语言( 通过代码修改源程序 ),可以对类创建的实例绑定任意的属性。
一般方法有两种:
(1)直接在类中声明
下面例子中的books,这个变量会被所有实例共享。所以在第一个实例tom中修改了books属性,同时也会修改bob的books属性
tips:注意 python 命名变量名 set(), list(), dictionary -> books NOT book_list

class Student:
    books = [] # 这些类共有的东西
tom = Student()
tom.books.append("Maching leading")
bob = Student()
print(bob.books)
# re = ['Maching leading']
# tom 的书会跑到 bob 里面

(2)在的类方法中间动态绑定

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age
s = Student('Gao', 30)
s.name = 'Mike'
s.ranking = 1
s.ranking
# 增加一个没有的属性

属性限制 slots

当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
但是有时候我们希望我们定义的类,在后续不能随心所欲的绑定属性。这时需要定义 slots 方法

class Student:
    __slots__ = ['name', 'age', 'school']
tom = Student()
tom.ranking = 0
# AttributeError: 'Student' object has no attribute 'ranking'

加保险实例:

class ProdAgent:
    __slots__ = ['length', 'height', 'weight']
    def __init__(self, length, height, weight):
        self.length = length
        self.height = height
        self.weight = weight

    def product(self):
        _product = 1
        for attr in self.__dir__():
            if attr.startswith('__'):
                continue
            if attr == "product":
                continue
            _product *= getattr(self, attr, 1)
        return _product
agent = ProdAgent(12, 1700, 6500)
agent.product()
# 如果不加入属性限制,增加 agent.color = 'red', 执行这句 _product *= getattr(self, attr, 1) 会得到好多 red
# redredredredredredredredredredredredredredredredredredredredredredredredredredredredredredredredredred

属性装饰器 @property @classmethod @staticmethod

property 是个神奇的装饰器,它可以用来保护一个属性,不被外部任意地修改;还可以用来作为动态的属性方法

# 保护属性
class Student:
    def __init__(self, name, age):
        self.name = name
        self._age = age

    @property
    def age(self):
        return self._age

tom = Student("tom", 20)
tom.name = "tom_modified"
print(tom.age) # 你可以读

# property的属性是不允许被直接修改的
tom.age = 21 # 但是你不能直接修改
# AttributeError: can't set attribute

使用 setter 修改

# 但是可以通过定义 setter 方法来允许属性被修改
class Student2():
    def __init__(self, name, age):
        self.name = name
        self._age = age
        self._age_modified_time = 0
    @property
    def age(self):
        return self._age 
    @age.setter
    def age(self, a):
        self._age = a  # how to assign this variable
        self._age_modified_time += 1 # 修改的次数
tom = Student2("tom", 20)
tom.name = "tom_modified"
print(tom.age)
# ----------- #
tom.age = 21
print(tom.age)
# 20
# 21

调用 class 对象的时候,见过很多,if name == ‘main‘ 这是必须的么?
主函数调用执行语句
self == object this

classmethod 是用来指定一个类的方法为类方法,一般要调用一个类定义的方法需要通过实例化出一个实例来进行调用,但是 classmethod 允许类的某个方法直接被调用,常用与工厂模式。

class Student:
    name = ""

class Agent:
    @classmethod # first usage 
    def modify_name(cls, obj):
        obj.name += "(modified)"

tom = Student()
tom.name = "tom"
print(tom.name)

# 直接调用类,不需要进行实例化
Agent.modify_name(tom)
print(tom.name)
class Student:
    initialed_num = 0 

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.initialed_num = 1
        Student.initialed_num += 1

    def say(self):
        print('I am {}'.format(self.name))

    @classmethod
    def say_we_have(cls):
        print('I am {}'.format(str(cls)))
        print('We have {} people'.format(cls.initialed_num)) 
        # self.initial -> Student.initial    


    # 增删改查 不用类本身的属性 staticmethod 声明为普通方法
    @staticmethod 
    def check(something):
        print('checking', something)
mike = Student('Mike', 20)
jhon = Student('Jhon', 30)
mike.say_we_have()
# I am <class '__main__.Student'>
# We have 2 people

多重继承

除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。
举个简单的例子,如示例代码1,C同时继承了A,和B的属性。示例2中优先继承了A的属性。
这种方法也常用在工厂模式中。

class A:
    def a(self):
        print("a of a")

class B:
    def b(self):
        print("b of b")

class C(A,B):
    pass

c = C()
c.a()
c.b()
# res
# a of a
# b of b

例题:假设在处理数据流程中,一般的处理流程为:数据清洗 -> 去重 -> 计算 -> 入库
但是对于不同的数据源可能数据清晰和去重不一样,如A数据源需要清晰方法A,去重方法A;B数据源需要清洗方法B,去重方法B;C数据源需要清洗方法A,去重方法B;
对于这样的流程我们可以抽象成这样的结构

class CleanerA:
    def clean(self):
        print("clean by A")

class CleanerB:
    def clean(self):
        print("clean by B")

class DropDuplicatesA:
    def drop(self):
        print("drop by A")

class DropDuplicatesB:
    def drop(self):
        print("drop by B")

class ProcessAgent():
    def clean(self):
        pass
    def drop(self):
        pass
    def process(self):
        print("processing")
    def run(self):
        self.clean()
        self.drop()
        self.process()

class ProcessorA(CleanerA, DropDuplicatesA, ProcessAgent):
    pass

class ProcessorB(CleanerB, DropDuplicatesB, ProcessAgent):
    pass

class ProcessorC(CleanerA, DropDuplicatesB, ProcessAgent):
    pass

a = ProcessorA()
a.run()

print('*'*8)
b = ProcessorB()
b.run()

print('*'*8)

c = ProcessorC()
c.run()
# res
# clean by A
# drop by A
# processing
# ********
# clean by B
# drop by B
# processing
# ********
# clean by A
# drop by B
# processing

类装饰器、属性装饰器

装饰器有好多种写法,一般常用的三种:函数装饰器、类装饰器、属性装饰器。
假设在做测试的时候需要汇报不同的bug,于是不同的Bug有对应不同的类。 但是需要在每个Bug类初始化时候做一些事情,这里就可以用到类的装饰器
类的装饰器其实与普通的装饰器也很类似,不同在于返回的是类对象
同样属性装饰器与一般装饰器类似,只是要主要的是参数需要多加一个实例
同样我们用Bug的例子,我们需要在装饰的方法上都打印个 “Wraning!”

def bug_warpper(func):
    def func_warp(instance, *args, **kwargs):
        print("waring!!! connect to host server!")
        return func(instance, *args, **kwargs)
    return func_warp

class Bug: 
    @bug_warpper
    def __init__(self):
        pass

    @bug_warpper
    def output(self):
        return "output"
bug = Bug()
bug.output()
# res
# waring!!! connect to host server!
# waring!!! connect to host server!
# 'output'

重载运算符

class Hero:
    def __init__(self, name, age, attact):
        self.name = name
        self.age = age
        self.attact = attact

    def __add__(self, another_hero):
        new_name = '-'.join([self.name, another_hero.name])
        new_age = max(self.age, another_hero.age)
        new_attact = self.attact + another_hero.attact

        new_hero = Hero(new_name, new_age, new_attact)

        return new_hero

    def __repr__(self):
        return 'I am: {}, with age: {}, attact-> {}'.format(self.name, self.age, self.attact)

    def __len__(self):
        return len(self.name)

请多多指教。

文章标题:Python 之面向对象

本文作者:顺强

发布时间:2019-09-04, 23:59:00

原始链接:http://shunqiang.ml/python-python-object/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏