I'm attempting to learn Python on my own,so, I got a piece of software I had written in C#, and attempted to re-write it in Python. Given the following class I have a few questions:
C#
sealed class Message
{
private int messageID;
private string message;
private ConcurrentBag <Employee> messageFor;
private Person messageFrom;
private string calltype;
private string time;
public Message(int iden,string message, Person messageFrom, string calltype,string time)
{
this.MessageIdentification = iden;
this.messageFor = new ConcurrentBag<Employee>();
this.Note = message;
this.MessageFrom = messageFrom;
this.CallType = calltype;
this.MessageTime = time;
}
public ICollection<Employee> ReturnMessageFor
{
get
{
return messageFor.ToArray();
}
}
In my class I have a thread-safe collection called messageFor, is there an equivalent in Python? If so, how do I implement it in a python class?
I also have a getter for my thread-safe collection? How would I go about doing the same in Python?
Does Python have an EqualsTo method to test equality between objects? Or the equivalent of this in Python?
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
Message testEquals = obj as Message;
if((System.Object)testEquals == null)
{
return false;
}
return (this.messageID == testEquals.messageID) && (this.message == testEquals.message) && (this.messageFor == testEquals.messageFor) && (this.messageFrom == testEquals.messageFrom) && (this.calltype == testEquals.calltype);
}
public bool Equals(Message p)
{
if ((Object)p == null)
{
return false;
}
return (this.messageID == p.messageID) && (this.message == p.message) && (this.messageFor == p.messageFor) && (this.messageFrom == p.messageFrom) && (this.calltype == p.calltype);
}
Can you make it sealed, so that no one can inherit from it?
What I've got so far:
class Message:
def __init__(self, messageID, message, callType):
self.messageID = messageID
self.message = message
self.callType = callType
答案 0 :(得分:4)
In my class I have a thread-safe collection called messageFor, is there an equivalent in Python? If so, how do I implement it in a python class?
In general, CPython's Global Interpreter Lock prevents crashes due to simultaneous access/modification, since you can't achieve truly simultaneous evaluation at all. So an ordinary list might be suitable, or you possibly may need a Queue to prevent higher-level race conditions. See Are lists thread-safe for more information.
I also have a getter for my thread-safe collection? How would I go about doing the same in Python?
You can emulate the behavior of C# getters/setters using the @property
decorator. See Python @property versus getters and setters for more information.
Does Python have an EqualsTo method to test equality between objects? Or the equivalent of this in Python?
You can define equality behavior using the __eq__
method. For example, your code might translate to something like:
class Thing:
#other methods go here...
def __eq__(self, other):
if not isinstance(other, Thing):
return False
return self.messageID == other.messageID and self.message == other.message #and... etc
Can you make it sealed, so that no one can inherit from it?
As far as I know, this is impossible. Python generally follows a "we're all adults here" philosophy. If a coder wants to inherit from a class or access an object's attributes, then they can; the best you can do is give a stern suggestion not to in the documentation (or put up small speed bumps, in the case of name mangling for "private" attributes).
答案 1 :(得分:0)
您有几个问题,所以让我们按照他们被问到的顺序来看看它们:
Bag
最接近的是collections.Counter
。但是,它不是为并发使用而设计的,并且具有与您熟悉的ConcurrentBag
类不同的方法。通过继承collection.Counter
并指定一个旨在使类具有线程安全性的自定义元类,可以轻松地重新创建用于Python的ConcurrentBag
类。property
装饰器在Python中复制线程安全集合的getter。由于您将信息设置为只读,因此这是最简单的实现方法。您可以在Message
类的初始化程序下方的代码中看到该属性。__eq__
方法作为该类的一部分。示例代码中显示的版本正好在上述属性之下。它首先检查要比较的实例的类型,然后查看除__time
之外的每个实例的所有其他属性。SealedMeta
,尝试从“密封”类继承的任何类都将导致RuntimeError
被引发。但请注意,任何有权访问您班级源代码的人都可以删除其密封名称。以下类和元类应该可以解决您遇到的问题。它们如下:
SealedMeta
是一个元类,可用于将类标记为已密封。尝试从密封类继承的任何类都将无法构造,因为将生成RuntimeError
异常。因为要求非常简单,所以这个元类比示例代码中显示的第二个要简单得多。Message
试图重新创建您在C#中编写的类。参数和属性的名称略有不同,但存在相同的基本思想。由于在Python中没有静态指定类型,因此在此类中只需要一个相等检查方法。请注意该类如何标记为顶部为(metaclass=SealedMeta)
的密封。AtomicMeta
是一个具有挑战性的元类,我不确定它是完全正确的。它不仅必须使类的方法成为线程安全的,它还必须处理该类继承的所有基础。由于旧式super
调用可能存在于这些基础中,因此元类会尝试修复它们所在的模块。ConcurrentBag
继承自collections.Counter
,因为它最像是来自其他语言的包。由于您希望该类是线程安全的,因此必须使用AtomicMeta
元类,以便将其及其父类的方法更改为原子操作。引入了to_array
方法来改进其API。没有进一步的延迟,这里是上面引用的代码。希望这对你和其他人都有帮助:
#! /usr/bin/env python3
import builtins
import collections
import functools
import inspect
import threading
class SealedMeta(type):
"""SealedMeta(name, bases, dictionary) -> new sealed class"""
__REGISTRY = ()
def __new__(mcs, name, bases, dictionary):
"""Create a new class only if it is not related to a sealed class."""
if any(issubclass(base, mcs.__REGISTRY) for base in bases):
raise RuntimeError('no class may inherit from a sealed class')
mcs.__REGISTRY += (super().__new__(mcs, name, bases, dictionary),)
return mcs.__REGISTRY[-1]
class Message(metaclass=SealedMeta):
"""Message(identifier, message, source, kind, time) -> Message instance"""
def __init__(self, identifier, message, source, kind, time):
"""Initialize all the attributes in a new Message instance."""
self.__identifier = identifier
self.__message = message
self.__source = source
self.__kind = kind
self.__time = time
self.__destination = ConcurrentBag()
@property
def destination(self):
"""Destination property containing serialized Employee instances."""
return self.__destination.to_array()
def __eq__(self, other):
"""Return if this instance has the same data as the other instance."""
# noinspection PyPep8
return isinstance(other, type(self)) and \
self.__identifier == other.__identifier and \
self.__message == other.__message and \
self.__source == other.__source and \
self.__kind == other.__kind and \
self.__destination == other.__destination
class AtomicMeta(type):
"""AtomicMeta(name, bases, dictionary) -> new thread-safe class"""
__REGISTRY = {}
def __new__(mcs, name, bases, dictionary, parent=None):
"""Create a new class while fixing bases and all callable items."""
final_bases = []
# Replace bases with those that are safe to use.
for base in bases:
fixed = mcs.__REGISTRY.get(base)
if fixed:
final_bases.append(fixed)
elif base in mcs.__REGISTRY.values():
final_bases.append(base)
elif base in vars(builtins).values():
final_bases.append(base)
else:
final_bases.append(mcs(
base.__name__, base.__bases__, dict(vars(base)), base
))
class_lock = threading.Lock()
# Wrap all callable attributes so that they are thread-safe.
for key, value in dictionary.items():
if callable(value):
dictionary[key] = mcs.__wrap(value, class_lock)
new_class = super().__new__(mcs, name, tuple(final_bases), dictionary)
# Register the class and potentially replace parent references.
if parent is None:
mcs.__REGISTRY[object()] = new_class
else:
mcs.__REGISTRY[parent] = new_class
source = inspect.getmodule(parent)
*prefix, root = parent.__qualname__.split('.')
for name in prefix:
source = getattr(source, name)
setattr(source, root, new_class)
return new_class
# noinspection PyUnusedLocal
def __init__(cls, name, bases, dictionary, parent=None):
"""Initialize the new class while ignoring any potential parent."""
super().__init__(name, bases, dictionary)
@staticmethod
def __wrap(method, class_lock):
"""Ensure that all method calls are run as atomic operations."""
@functools.wraps(method)
def atomic_wrapper(self, *args, **kwargs):
with class_lock:
try:
instance_lock = self.__lock
except AttributeError:
instance_lock = self.__lock = threading.RLock()
with instance_lock:
return method(self, *args, **kwargs)
return atomic_wrapper
# noinspection PyAbstractClass
class ConcurrentBag(collections.Counter, metaclass=AtomicMeta):
"""ConcurrentBag() -> ConcurrentBag instance"""
def to_array(self):
"""Serialize the data in the ConcurrentBag instance."""
return tuple(key for key, value in self.items() for _ in range(value))
在做了一些研究之后,你可能会发现有一些替代方法可以用来封印课程。在Java中,类标记为final
,而不是使用sealed
关键字。搜索替代关键字可以将您带到Martijn Pieters' answer,这为Python中的密封类引入了一个稍微简单的元类。您可以使用以下元类,其启发方式与代码中当前使用SealedMeta
的方式相同:
class Final(type):
"""Final(name, bases, dictionary) -> new final class"""
def __new__(mcs, name, bases, dictionary):
"""Create a new class if none of its bases are marked as final."""
if any(isinstance(base, mcs) for base in bases):
raise TypeError('no class may inherit from a final class')
return super().__new__(mcs, name, bases, dictionary)
如果要密封一个类并阻止其他人继承它,Final
元类很有用,但如果一个类的属性是最终的,则需要采用不同的方法。在这种情况下,Access
元类可能非常有用。它的灵感来自this answer问题Prevent function overriding in Python。在下面显示的元类中采用了一种改进的方法,该方法旨在考虑所有类型的属性。
import collections
import threading
# noinspection PyProtectedMember
class RLock(threading._RLock):
"""RLock() -> RLock instance with count property"""
@property
def count(self):
"""Count property showing current level of lock ownership."""
return self._count
class Access(type):
"""Access(name, bases, dictionary) -> class supporting final attributes"""
__MUTEX = RLock()
__FINAL = []
@classmethod
def __prepare__(mcs, name, bases, **keywords):
"""Begin construction of a class and check for possible deadlocks."""
if not mcs.__MUTEX.acquire(True, 10):
raise RuntimeError('please check your code for deadlocks')
# noinspection PyUnresolvedReferences
return super().__prepare__(mcs, name, bases, **keywords)
@classmethod
def final(mcs, attribute):
"""Record an attribute as being final so it cannot be overridden."""
with mcs.__MUTEX:
if any(attribute is final for final in mcs.__FINAL):
raise SyntaxError('attributes may be marked final only once')
mcs.__FINAL.append(attribute)
return attribute
def __new__(mcs, class_name, bases, dictionary):
"""Create a new class that supports the concept of final attributes."""
classes, visited, names = collections.deque(bases), set(), set()
# Find all attributes marked as final in base classes.
while classes:
base = classes.popleft()
if base not in visited:
visited.add(base)
classes.extend(base.__bases__)
names.update(getattr(base, '__final_attributes__', ()))
# Verify that the current class does not override final attributes.
if any(name in names for name in dictionary):
raise SyntaxError('final attributes may not be overridden')
names.clear()
# Collect the names of all attributes that are marked as final.
for name, attribute in dictionary.items():
for index, final in enumerate(mcs.__FINAL):
if attribute is final:
del mcs.__FINAL[index]
names.add(name)
break
# Do a sanity check to ensure this metaclass is being used properly.
if mcs.__MUTEX.count == 1 and mcs.__FINAL:
raise RuntimeError('final decorator has not been used correctly')
mcs.__MUTEX.release()
dictionary['__final_attributes__'] = frozenset(names)
return super().__new__(mcs, class_name, bases, dictionary)
作为如何使用Access
元类的演示,此示例将引发SyntaxError
:
class Parent(metaclass=Access):
def __init__(self, a, b):
self.__a = a
self.__b = b
@Access.final
def add(self):
return self.__a + self.__b
def sub(self):
return self.__a - self.__b
class Child(Parent):
def __init__(self, a, b, c):
super().__init__(a, b)
self.__c = c
def add(self):
return super().add() + self.__c