哈Ha 最近,我为设计疯狂-访问修饰符和接口,然后将其移植到Python编程语言。 我问凯特-我分享结果以及它的工作方式。 对于那些感兴趣的人,在文章末尾有一个指向Github上的项目的链接。
访问修饰符
访问修饰符将对象(其类的方法或子类)的访问限制为对其父类的方法的访问。 使用访问修饰符有助于将数据隐藏在该类中,从而使外界都不会干扰该类的工作。
私有方法仅在类内部,
受保护的 (内部)-类内部和子类中可用。
如何在Python中实现私有和受保护的方法
剧透-在达成协议的水平上,成年人根本不会在教室外打电话给他们。 在使用私有方法之前,您需要先写一个双下划线,然后再保护一个。 尽管方法具有“有限”的访问权限,但您仍然可以访问这些方法。
class Car: def _start_engine(self): return "Engine's sound." def run(self): return self._start_engine() if __name__ == '__main__': car = Car() assert "Engine's sound." == car.run() assert "Engine's sound." == car._start_engine()
可以确定以下缺点:
- 如果_start_engine方法更新了一些类变量或保持了状态,而不仅仅是返回了“哑计算”,则可能会破坏某些东西以备将来使用该类。 您不允许自己修理汽车引擎中的东西,因为那样您将无处可去,对吗?
- 前一个问题的重点-确保可以“安全”(调用该方法不会损害类本身)使用受保护的方法-您需要研究其代码并花时间。
- 库的作者希望没有人使用您在项目中使用的类的受保护的私有方法。 因此,他们可以在任何发行版中更改其实现(由于向后兼容,这不会影响公共方法,但会给您带来麻烦)。
- 该课程的作者,您的同事,希望您不会在他创建的课程之外使用受保护的或私有的方法来增加项目的技术负担。 毕竟,要重构或修改它(私有类方法)的人必须确保(例如,通过测试)他所做的更改不会破坏您的代码。 而且,如果他们破坏了它,他将不得不花时间尝试解决这个问题(用拐杖,因为他昨天需要它)。
- 也许您要确保其他程序员在代码审查和“争取”时不要使用受保护的方法或私有方法,所以要花一些时间。
如何使用库实现受保护的方法
from accessify import protected class Car: @protected def start_engine(self): return "Engine's sound." def run(self): return self.start_engine() if __name__ == '__main__': car = Car() assert "Engine's sound." == car.run() car.start_engine()
尝试在类之外调用
start_engine方法将导致以下错误(根据访问策略,该方法不可用):
Traceback (most recent call last): File "examples/access/private.py", line 24, in <module> car.start_engine() File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/main.py", line 92, in private_wrapper class_name=instance_class.__name__, method_name=method.__name__, accessify.errors.InaccessibleDueToItsProtectionLevelException: Car.start_engine() is inaccessible due to its protection level
使用库:
- 您无需使用难看的(主观)下划线或双下划线。
- 您将获得一种漂亮的(主观的)方法来在代码中实现访问修饰符- 私有和受保护的修饰符。
- 将责任从人转移到口译员。
运作方式:
- 私有或受保护的装饰器-最“高级”的装饰器, 在被声明为私有或受保护的访问修饰符的class方法之前触发 。
- 装饰器使用内置的检查库,从调用堆栈-inspect.currentframe()中检索当前对象。 该对象具有对我们有用的以下属性:命名空间(本地)和从调用堆栈(使用access修饰符调用方法的对象)到上一个对象的链接。
(非常简化的插图)
- inspect.currentframe()。f_back-使用此属性检查调用堆栈中的上一个对象是否在类主体中。 为此,请查看名称空间-f_locals 。 如果名称空间中有一个self属性,则在类内部调用该方法,如果没有,则在类外部调用该方法。 如果您在类外调用带有私有或受保护的访问修饰符的方法,则将出现访问策略错误。
介面
接口是与实现接口的类进行交互的契约。 接口包含方法签名(函数的名称,输入参数),以及实现接口的类(在签名之后)实现逻辑。 总结一下,如果两个类实现相同的接口,则可以确保这些类的两个对象都具有相同的方法。
例子
我们有一个使用
存储对象创建新用户的
User类。
class User: def __init__(self, storage): self.storage = storage def create(self, name): return storage.create_with_name(name=name)
您可以使用
DatabaseStorage.create_with_name将用户保存到
数据库 。
class DatabaseStorage: def create_with_name(self, name): ...
您可以使用
FileStorage.create_with_name将用户保存到文件中。
class FileStorage: def create_with_name(self, name): ...
由于类的
create_with_name方法的签名(名称,参数)是相同的
-User类不必担心如果它们具有相同的方法将替换为哪个对象。 如果
FileStorage和
DatabaseStorage类实现相同的接口(即,它们受合同约束以定义一些内部具有逻辑的方法),则可以实现此目的。
if __name__ == '__main__': if settings.config.storage = FileStorage: storage = FileStorage() if settings.config.storage = DatabaseStorage: storage = DatabaseStorage() user = User(storage=storage) user.create_with_name(name=...)
如何使用库使用接口
如果类实现接口,则该类必须包含interface的
所有方法 。 在下面的示例中,HumanInterface接口包含eat方法,Human类实现了该方法,但未实现eat方法。
from accessify import implements class HumanInterface: @staticmethod def eat(food, *args, allergy=None, **kwargs): pass if __name__ == '__main__': @implements(HumanInterface) class Human: pass
该脚本将退出,并显示以下错误:
Traceback (most recent call last): File "examples/interfaces/single.py", line 18, in <module> @implements(HumanInterface) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 66, in decorator interface_method_arguments=interface_method.arguments_as_string, accessify.errors.InterfaceMemberHasNotBeenImplementedException: class Human does not implement interface member HumanInterface.eat(food, args, allergy, kwargs)
如果类实现接口,则该类必须包含该接口的所有方法,
包括所有传入参数 。 在下面的示例中,HumanInterface接口包含eat方法,该方法将4个参数用作输入,Human类实现了该方法,但仅使用1个参数实现了eat方法。
from accessify import implements class HumanInterface: @staticmethod def eat(food, *args, allergy=None, **kwargs): pass if __name__ == '__main__': @implements(HumanInterface) class Human: @staticmethod def eat(food): pass
该脚本将退出,并显示以下错误:
Traceback (most recent call last): File "examples/interfaces/single_arguments.py", line 16, in <module> @implements(HumanInterface) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 87, in decorator interface_method_arguments=interface_method.arguments_as_string, accessify.errors.InterfaceMemberHasNotBeenImplementedWithMismatchedArgumentsException: class Human implements interface member HumanInterface.eat(food, args, allergy, kwargs) with mismatched arguments
如果一个类实现一个接口,则该类必须包含该接口的所有方法,包括传入参数和
访问修饰符 。 在下面的示例中,HumanInterface接口包含私有eat方法,而Human类实现了该方法,但未实现eat方法的私有访问修饰符。
from accessify import implements, private class HumanInterface: @private @staticmethod def eat(food, *args, allergy=None, **kwargs): pass if __name__ == '__main__': @implements(HumanInterface) class Human: @staticmethod def eat(food, *args, allergy=None, **kwargs): pass
该脚本将退出,并显示以下错误:
Traceback (most recent call last): File "examples/interfaces/single_access.py", line 18, in <module> @implements(HumanInterface) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 77, in decorator interface_method_name=interface_method.name, accessify.errors.ImplementedInterfaceMemberHasIncorrectAccessModifierException: Human.eat(food, args, allergy, kwargs) mismatches HumanInterface.eat() member access modifier.
一个类可以实现几个(数量不限)接口。 如果一个类实现多个接口,则该类必须包含
所有接口的所有方法,包括传入参数和访问修饰符 。 在下面的示例中,Human类实现了HumanBasicsInterface接口的eat方法,但没有实现HumanSoulInterface接口的love方法。
from accessify import implements class HumanSoulInterface: def love(self, who, *args, **kwargs): pass class HumanBasicsInterface: @staticmethod def eat(food, *args, allergy=None, **kwargs): pass if __name__ == '__main__': @implements(HumanSoulInterface, HumanBasicsInterface) class Human: def love(self, who, *args, **kwargs): pass
该脚本将退出,并显示以下错误:
Traceback (most recent call last): File "examples/interfaces/multiple.py", line 19, in <module> @implements(HumanSoulInterface, HumanBasicsInterface) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 66, in decorator interface_method_arguments=interface_method.arguments_as_string, accessify.errors.InterfaceMemberHasNotBeenImplementedException: class Human does not implement interface member HumanBasicsInterface.eat(food, args, allergy, kwargs)
杀手级功能-接口方法可以“声明”实现该类的方法应该“抛出”的错误。 在下面的示例中,“声明”“ HumanInterface”接口的“ love”方法应引发“ HumanDoesNotExistError”异常,并且
“ HumanAlreadyInLoveError”,但“ Human”类的“ love”方法不会“抛出”其中一个。
from accessify import implements, throws class HumanDoesNotExistError(Exception): pass class HumanAlreadyInLoveError(Exception): pass class HumanInterface: @throws(HumanDoesNotExistError, HumanAlreadyInLoveError) def love(self, who, *args, **kwargs): pass if __name__ == '__main__': @implements(HumanInterface) class Human: def love(self, who, *args, **kwargs): if who is None: raise HumanDoesNotExistError('Human whom need to love does not exist')
该脚本将退出,并显示以下错误:
Traceback (most recent call last): File "examples/interfaces/throws.py", line 21, in <module> @implements(HumanInterface) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/accessify/interfaces.py", line 103, in decorator class_method_arguments=class_member.arguments_as_string, accessify.errors.DeclaredInterfaceExceptionHasNotBeenImplementedException: Declared exception HumanAlreadyInLoveError by HumanInterface.love() member has not been implemented by Human.love(self, who, args, kwargs)
总结一下,使用库:
- 您可以实现一个或多个接口。
- 接口与访问修饰符结合在一起。
- 您将获得接口和抽象类( Python中的abc模块 )的分离,现在(如果您这样做)则不需要将抽象类用作接口。
- 与抽象类相比。 如果尚未从接口定义方法的所有参数,则使用抽象类将收到错误-否。
- 与抽象类相比。 使用接口,在创建类时(编写类并调用* .py文件时)会出错。 在抽象类中,在调用类对象的方法的阶段已经出现错误。
运作方式:
- 使用工具装饰器中的内置检查库, 可获得该类的所有方法及其接口-inspect.getmembers() 。 方法的唯一索引是其名称和类型(静态方法,属性等)的组合。
- 并使用inspect.signature() ,该方法的参数。
- 我们遍历该接口的所有方法,并查看类中是否存在实现该接口的方法(通过唯一索引),传入的参数是否相同,访问修饰符是否相同,该方法是否实现了接口方法中声明的错误。
感谢您对本文的关注。
链接到Github上的项目 。