Create multiple buttons dynamically in MFC

Create multiple continuous buttons dynamically in MFC and release them. A program written by yourself! Very valuable!

File: n459.com/file/25127180-479632270 Access password: 551685

The following are irrelevant:

-------------------------------------------Dividing line----- ----------------------------------------

Let's first briefly review the copy in Python.

Copy has corresponding functions in many languages, and Python is no exception. There are two copy functions in Python, one is copy and the other is deepcopy. That is to say, deep copy and shallow copy, the difference between the two is also very simple, in short, shallow copy will only copy the parent object, not the child objects in the parent object.

Let's look at an example. In the figure below, b is a shallow copy of a. We can see that after a 5 is inserted in a[2], there is also an additional 5 in b. Because their subscript 2 stores the same reference, when a is inserted, the same change occurs in b. We can also see that when we change a[0], there is no corresponding change in b. Because a[0] is a number, the number is a value stored directly in the underlying type rather than a reference.

Corresponding to shallow copy is deep copy. We can see that when an element is inserted into a[2], the deep copy of b will not change accordingly.

Memento
uses copy, we can implement the memento function, its role is to back up the object. In Python, for an object obj, all its members and functions and other information are all stored in the dict of obj.__dict__. In other words, if we make a copy of the __dict__ of an object, it is actually equivalent to a copy of the object.

By using copy, we can easily implement the memento function, let's look at the code first.

from copy import copy, deepcopy

def memento(obj, deep=False):
state = deepcopy(obj.dict) if deep else copy(obj.dict)

def restore():
    obj.__dict__.clear()
    obj.__dict__.update(state)

return restore

Memento is a higher-order function, and the result it returns is the execution function, not the specific execution result. If you are not familiar with higher-order functions, you can review the related content of higher-order functions in Python.

The logic is not difficult to understand. The parameters passed in are an obj object and a bool-type flag. Flag means to use deep copy or shallow copy, and obj is the object we need to take a corresponding snapshot or archive. We hope to restore the content on the basis of the object frame unchanged, so the scope of our copy is very clear, that is, obj. dict , which stores all the key information of the object.

Let's take a look at the restore function. The content is actually very simple, with only two lines. The first line is to clear the content in obj's current __dict__, and the second step is to restore it with the previously saved state. In fact, restore performs a function of rolling back obj. Let's walk through the whole process. We run the memento function to get the restore function. When we execute this function, the contents of obj will be rolled back to the state when memento was executed last time.

After understanding the logic in memento, we are not far from realizing our affairs. We have two implementation methods for transactions, one is through objects, the other is through decorators, let's talk about them one by one.

Transaction object The object
-oriented implementation is relatively simple, and it is similar to our usual transaction process. Two functions should be provided in the Transaction object, one is commit and the other is rollback. That is to say, when we execute successfully, we execute commit and take a snapshot of the results of the execution. If the execution fails, rollback will roll back the result of the object to the state of the last commit.

After we understand the memento function, we will find that commit and rollback correspond to the execution of the memento function and the execution of the restore function. So it is not difficult for us to write the code:

class Transaction:

deep = False
states = []

def __init__(self, deep, *targets):
    self.deep = deep
    self.targets = targets
    self.commit()

def commit(self):
    self.states = [memento(target, self.deep) for target in self.targets]

def rollback(self):
    for a_state in self.states:
        a_state()

Since we may need more than one object for transaction, the targets here are designed in the form of an array.

Transaction decorator
We can also implement the transaction as a decorator so that we can use it through annotations.

The code principle here is also the same, except that the implementation logic is based on decorators. If you are familiar with decorators, it is not difficult to understand. The args[0] here is actually an instance of a certain class, which is the subject of the transaction we need to guarantee.

from functools import wraps

def transactional(func):
@wraps(func)
def wrapper(*args, **kwargs):
# args[0] is obj
state = memento(args[0])
try:
func(*args, **kwargs)
except Exception as e:
state()
raise e
return wrapper
This is how a regular decorator is written. Of course, we can also use classes to implement decorators. In fact, the principle is similar, but some details are different.

class Transactional:

def __init__(self, method):
    self.method = method

def __get__(self, obj, cls):
    def transaction(*args, **kwargs):
        state = memento(obj)
        try:
            return self.method(*args, **kwargs)
        except Exception as e:
            state()
            raise e
    return transaction

When we add this annotation to a certain class method, when we execute obj.xxx, the __get__ method in the Transactional class will be executed instead of obtaining the Transactional class. And pass in obj and the type corresponding to obj as parameters, which is the meaning of obj and cls here. This is the conventional practice of using classes to implement decorators. Let's post the conventional code to learn more.

class Wrapper:
def init(self, func):
wraps(func)(self)

def __call__(self, *args, **kwargs):
    return self.__wrapped__(*args, **kwargs)

def __get__(self, instance, cls):
    if instance is None:
        return self
    else:
        return types.MethodType(self, instance)

This is a case where a class is used to implement a decorator. We can see that the __get__ function returns self, which means it returns the Wrapper class. A class is usually not directly executable. In order to make it executable, a __call__ function is implemented for it. It doesn't matter if you still don't understand, you can ignore this part. It is also not common to implement decorators with classes, and we can be familiar with higher-order functions.

Actual combat
Finally, let’s look at a practical application example. We have implemented a NumObj class that is compatible with the use of the above two transactions. You can compare and see the difference.

class NumObj:
def init(self, value):
self.value = value

def __repr__(self):
    return '<%s, %r>' % (self.__class__.__name__, self.value)

def increment(self):
    self.value += 1

@transactional
def do_stuff(self):
    self.value += '111'
    self.increment()

if name == ‘main’:
num_obj = NumObj(-1)

a_transaction = Transaction(True, num_obj)

Use Transaction

try:
    for i in range(3):
        num_obj.increment()
        print(num_obj)

    a_transaction.commit()
    print('----committed')
    for i in range(3):
        num_obj.increment()
        print(num_obj)
    num_obj.value += 'x'
    print(num_obj)
except Exception:
    a_transaction.rollback()
    print('----rollback')

print(num_obj)

Use Transactional

print('-- now doing stuff')
num_obj.increment()

try:
    num_obj.do_stuff()
except Exception:
    print('-> doing stuff failed')
    import sys
    import traceback
    traceback.print_exc(file=sys.stdout)

print(num_obj)

From the code, it is not difficult to find that for Transaction, which is an object-oriented implementation, we need to create an additional Transaction instance to control whether to perform rollback in the try catch. The way of using annotations is more flexible, it will automatically perform rollback when execution fails, and does not require too many additional operations.

Generally speaking, we prefer the way of using annotations, because this way is more concise and clean, more pythonic, and can reflect the power of Python. The first method seems quite satisfactory, but the advantage is that it is more readable and the code implementation is less difficult. If you need to use it in your actual work, you can choose according to your actual situation. Both are good methods.

Guess you like

Origin blog.csdn.net/gumenghua_com1/article/details/112824293