Custom numeric data type for Python 3 with some additional properties
Project description
Cond - Numeric data type
Cond is a custom numeric data type for Python 3. It supports most operations that are legal on a regular data type, while adding some new properties. It can be installed using pip3 install cond.
The idea
The basic idea of a Cond object, is that it can hold various options at once, but only represent one of them. That option is called the main option.
Importing
The module is called cond, so it can be imported using import cond. The only three, public members of the module that are available are Cond, which is the class that is used to instantiate objects, LinkedCond, which is a modified version of the Cond class, and require, which is the function used to set "limitations" for Cond objects. Some other functions will also be imported, but these are implementation functions (indicated with a trailing underscore). All the following code assumes that the line from cond import Cond, LinkedCond, require has been called.
Initializing
-
Arguments
To initialize a Cond object, simply use the syntax
x = Cond(*args). In this case,argsis a list that contains an arbitrary amount of elements, all of the same numeric, built-in data type (e.g. integer). Of course, the arguments can also be passed as numeric literals,x = Cond(1, 5, 10). Whenever a Cond object is created in this manner, the main option of the object, which essentially represents it, will always be chosen to be the first passed argument, unless otherwise specified. At least one argument must be passed when a Cond object is created, unless therangekeyword is included (discussed below). -
Keyword arguments
There are currently two optional keyword arguments that can be used for the initialization of a Cond object. These are
mainposandrange. The former is used to specify the index of the main option of the object. For example,x = Cond(1, 5, 10, mainpos = 1)will initialize a Cond object with the options 1, 5 and 10, but the option that initially represents the object will be the second one (5). The latter is used to specify a range, which the Cond object will use to generate options. Therangekeyword argument has to either be a single positive integer (in which case all the numbers from 0 to it will be included), or a tuple, where the first argument specifies the start, the second argument specifies the end, and the (optional) third argument specifies the step. If this keyword argument is included, it is optional to also include arguments, as long as the range created by the numbers passed isn't empty.
Basic Operations
A Cond object will operate just like the numeric type that it represents in basic operations. The value that will be used for any actual operation will be the main value of the object. For instance:
x = Cond(5, 17)
y = x + 3
print(y)
OUTPUT:
8
The reason the above output is 8 for this, is that the main option of x is 5 (because it was passed first, and no mainpos was specified), so that value will be used for any operations. If, in the above code, the line where the Cond object is initialized was replaced with x = Cond(17, 5), or even, x = Cond(5, 17, mainpos = 1), the resulting value of y would be 20, because in both cases the main option of x would be 17. This works similarly for all other operations, including subtraction, multiplication, division, exponentiation, bitwise operations, etc.
Basic Evaluation
Just like in basic operations, a Cond object will also act in the same way as the numeric type that it represents in evaluation statements. For example:
x = Cond(11.5, 20.3)
y = 11.5
z = 20.3
print(x == y)
print(x == z)
OUTPUT:
True
False
As expected, the above code yields True and False. This is because, as stated before, x here is represented by the float 11.5, whereas 20.3 is just an option. So when x is evaluated to be equal to y, which is also 11.5, that yields 1, whereas when it is evaluated to be equal to z, which is 20.3, that yields 0.
Incrementing & Decrementing
A Cond object also has the ability to be incremented, decremented, or have any other syntax of the type [operation_sign]= be used on it. For example, dividing a Cond object's main option by 2 can be done using x /= 2, or for an integer, x >>= 1. Note that the former will only work if the options of x are of type float. If they are not, and in general, whenever an operation attempts to change the main option of a Cond object to a different type, TypeChangeError is thrown. The latter is a right shift by 1 bit and therefore, equivalent to x //= 2, for an integer number. Here, it is also important to note that, while for a regular number, x += 1 is completely equivalent to x = x + 1, for a Cond object, these are two completely different statements. In fact, the latter should be used with caution. The reason for this, is that stating x += 1, is essentially saying "increment the main option of x by 1", whereas stating x = x + 1 is saying "take 1, add it to the main value of the Cond object x and store the result in x". However, since the resulting value (x + 1) will be a numeric type, by default, x will also be set to be a numeric type. Therefore, the Cond object x will be overwritten by an integer, which will use the name, x, after the statement, and will have the final value that the Cond object would have had. This is demonstrated by this example:
x = Cond(5)
x += 1
print(x, type(x))
x = x + 1
print(x, type(x))
OUTPUT:
6 <class 'cond.Cond'>
7 <class 'int'>
Iterable Properties
A Cond object, even though it is represented by one main option, can hold many options at one time. This allows it to have properties that are usually found in iterables, such as lists and tuples. Firstly, calling len(x) will return the amount of options that the Cond object currently holds. Using bracket syntax, it is possible to view the option at a specific index; in addition to that, assigning and deleting options is possible, unless the given index corresponds to the main option. A Cond object can be used just like any other iterable in a for loop, looping through its options. The in syntax can also be used to check whether a given value is inside the list of options of the Cond object. Adding a new option to a Cond object can be achieved by using the x.append(value) syntax, just like one would use in a list. Similarly, x.remove(value) and x.index(value) are also valid expressions. Finally, calling x.all() will return a list of all the options that x currently holds. The below code snippet demonstrates the usage of all of these properties.
x = Cond(-8, 14, 3)
print("x length: ", len(x))
print("Third option: ", x[2])
x[2] = -x[2]
print("Third option: ", x[2])
del x[2]
print("All x options using for loop:")
for option in x:
print(option)
print(5 in x)
x.append(5)
x.remove(14)
print("Index of 5 in x: ", x.index(5))
print("All x options: ", x.all())
print("Main x option: ", x)
OUTPUT:
x length: 3
Third option: 3
Third option: -3
All x options using for loop:
-8
14
False
Index of 5 in x: 1
All x options: [-8, 5]
Main x option: -8
LinkedCond objects
LinkedCond objects can be instantiated just like Cond objects. The only difference between them and Cond objects, is that they have a "history"; in other words, they "remember" the limitations that are applied on them. An implication of this, is that they can be linked to each other; this is why they are called LinkedCond objects. LinkedCond objects, and their differences with Cond objects, are discussed later.
The require function
-
Cond objects
As mentioned above, the require function is the function which applies "limitations" to Cond objects and changes their main options, based on those limitations. In reality, the require function does nothing more than brute-forcing solutions until it potentially gets a correct one. More specifically, the syntax is:
require(expression, cond_objects, eval_sign, eval_number). Following is the explanation for each of these arguments.- expression: the expression is actually a string representation of an algebraic expression. In essence, it is the "left part" of the equation. Each Cond object that is part of the expression must be represented with a single-digit letter. Just as in regular Python, addition is represented by the "+" symbol, subtraction by the "-" symbol, multiplication by the "*" symbol, division by the "\" symbol and modulo by the "%" symbol. Contrary to how it works in regular Python, exponentiation is represented by the "^" symbol (though "**" can also be used). In addition to this, the multiplication symbol can be skipped in cases where algebra allows it (between single-letter variables, numbers or variables followed by parenthesis, numbers followed by variables, etc).
- cond_objects: this argument can either be a single Cond object, or a tuple containing multiple of them. However, the amount of Cond objects passed through this argument must be exactly equal to the amount of variables that the expression contains. The correspondence between variables and Cond objects is 1-1, meaning the first variable is paired with the first object, the second with the second, and so on. This means that, if the expression was
"x - y"and the tuple was(y, x), the actual operation that the function would attempt to satisfy would bey - xand notx - y, because the object y was written first, so it corresponds to the first available variable, x. In essence, variable names inside the expression are no more than conventions -they don't represent any actual variable names. - eval_sign: this argument is a string of the evaluation sign. This can be either one of: "=", ">", ">=", "<", "<=", "!=".
- eval_number: this argument is a numeric value, representing the "right side" of the equation. This can be any built-in numeric value, or it can be of type Cond. However, note that if type Cond is used, it will not be edited in any way. It will simply be used for its value and not take part in the actual expression. An expression cannot be used for this argument, therefore any equation should be solved so that there is only a single numeric value on the right side of it before using the function. This argument can also be a tuple of multiple numeric types, but only if the eval_sign argument is "!=".
The full equation can be recreated by substituting the variable names with the Cond object names, with the correct correlation and then appending the sign and the evaluation number at the end.
The require function will return
Trueif any combination of existing options for each included Cond object is found, which satisfies the given equation and will change the main value of the object to that which was found. If no combination of values that satisfy the equation are found,Falseis returned and no changes are made onto the Cond objects. Following are some examples of the require function's use.
x = Cond(1, 5, 10, 25) res = require("x", x, ">", 5) print(res) print(x)
OUTPUT:
True 10In this example, it is essentially requested that x be greater than 5. The function searched through the values, found that the value 10, which is the third option of the object, is greater than 5 and set that option to be the main option of the object. Because it was successful in satisfying the equation, it returned
True.x = Cond(-5, 10, 102, 54) y = Cond(4, 12, 66) z = Cond(3, 0, -1) res = require(r"xy/(2z) + 15%x - 3y", (x, y, z), "=", 8) print(res) print(x, y, z)
OUTPUT:
False -5 4 3In this example, no combination is found that satisfies the expression. Therefore, the function returned
Falseand the Cond objects passed kept their initial main options. Note: raw string was used instead of skipping the%character.res = require(r"15a - a^2 - b", (x, y), ">", 1) print(res) print(x, y)
OUTPUT:
True 10 4(
x,yused from Example 2): In this example, notice that x is not passed twice. Even though the variable a appears twice before the variable b, once the object x is mapped to the variable a, whenever a is found again, it is dismissed. Therefore, it is safe to pass y afterwards, so it is mapped to b, without worrying about passing x twice. Note: passing x twice would actually cause an error. -
LinkedCond objects
The require function can take LinkedCond objects instead of Cond objects as arguments. However, note that both LinkedCond and Cond objects cannot be passed as arguments in a single require call. This is explained further later on. The syntax for the require function, when it is run with LinkedCond objects, is exactly the same as that for when it is run with Cond objects. For example:
a = LinkedCond(1, 2, 3) res = require("x", a, ">", 2) print(res) print(a)
OUTPUT:
True 3Here, the LinkedCond object will act exactly like a Cond object. However, consider that the following code was run after the above code:
res = require("x", a, "<", 2) print(res) print(a)
OUTPUT:
False 3If we had initially made
abe an instance of the Cond class, the above code would have printed outTrueand1. For LinkedCond objects, this is not the case; because a LinkedCond object will remember previous require calls onto it, it is impossible to haveabe smaller than 2, whenamust already be greater than2. In other words, after the first require call,acannot ever be smaller than or equal to 2. Since that is what we request with the second require call,Falsewill be returned. Multiple LinkedCond objects can also be passed to a single require call. This will, in a way, link them. What this actually means, is that the main option of each of these objects will not only depend on require calls onto them, but also on require calls onto the objects that they are linked with. A big difference between Cond and LinkedCond objects, is that a Cond object can never have its main option altered, unless it is directly passed to require, using thecond_objectsargument; this is untrue for LinkedCond objects. It is possible for their main option to change indirectly. This would have to occur via a require call, to which a LinkedCond object, that is linked to them, is passed. An example of this follows.a = LinkedCond(range = 10) b = LinkedCond(range = 10) res = require("xy", (a, b), "=", 24) #a*b = 24 print(res) print(a, b) res = require("x", b, "<", 6) print(res) print(a, b)
OUTPUT:
True 3 8 True 8 3In the above example, notice that even though the second require call only contained
bin the passed objects,awas also altered. This is the reason whybis considered linked toa: it can be indirectly affected. When a require call attempts to changea, it will also attempt to changebso that the new limitation forais satisfied, whereas the previous limitations, which includedb, are also satisfied. Consider a situation where we wanted, just like before, the product ofaandbto be 24, however we also neededato be greater thanb. As shown,ais not greater thanbwhen the initial require call, for the product, is made. Even though it could be, if we really wanted to ensure that it is, we could do the following:a = LinkedCond(range = 10) b = LinkedCond(range = 10) res = require("xy", (a, b), "=", 24) #a*b = 24 print(res) print(a, b) res = require("x - y", (a, b), ">", 0) #x-y > 0 => x > y print(res) print(a, b)
OUTPUT:
True 3 8 True 8 3Here, just like before, it is first requested that the product of the two LinkedCond objects be equal to 24. Since the options for both objects are the digits, it is found that, if
abecomes 3 andbbecomes 8, the product will be 24. The second require call makes sure that the difference ofawithbis greater than 0. This is the correct way to require thatabe greater thanb. If that require call was replaced withrequire("x", a, ">", b), it would returnFalse. This is because, as stated before, objectbwas passed as a final argument, meaning it is the evaluation number. For this reason, it will only be used for its value and will not take part in the expression. The function will attempt to makeagreater than the value ofb, which is 8. In other words, it will attempt to makeabe 9; this won't work, since it must also be true that the product ofawithbis 24, which is impossible fora = 9, no matter what the main optionbis. Nevertheless, when the require call is written as it was in the code snippet, it makes sure that both the values ofaandbcan be re-evaluated, so matching values can be found. The second solution,a = 8andb = 3satisfies both equations; the function might also, in this case, pick the valuesa = 6andb = 4. This would also be correct in this case.
Additional LinkedCond properties
LinkedCond objects share all their methods with Cond objects, except for two extra ones:
- The first new method is
getlims(). This method will return a set, containing all the limitations that currently apply for the object. As for the names that will be used in place of the single-letter variables, it will be attempted to replicate the variable names that have been used. For example:obj = LinkedCond(range = 20) require("x", obj, ">", 11) print(obj.getlims())
OUTPUT:
In place of{"obj > 11"}x,obj, which is the variable name of the object, was used. This is done so that it is easier to see which objects the limitation is really referring to. Nevertheless, above, it was stated that it will be attempted to replicate the variable names. This is because, in some cases, it will be impossible to replicate those names. Consider the following:def func(): obj = LinkedCond(range = 20) require("x", obj, ">", 11) return [obj] obj_list = func() print(obj_list[0].getlims()) for v in obj_list: print(v.getlims())
OUTPUT:
Here, after the function{"obj_list[0] > 11"} {"v > 11"}funcquits, all the variables declared in its scope are destroyed; it is impossible to get the variable nameobjafter that. However, since the object is saved inside of the list namedobj_list, when using thegetlimsmethod on it, the name of the list that contains it followed by brackets and the index will be used instead. When iterating through the list, the name of the temporary variable will be used. The only case in which the single-letter variable names will be used, is when it is impossible to find variable names for all the included objects. For example, consider the following:def func(): obj = LinkedCond(range = 10) obj2 = LinkedCond(range = 20) require("xy", (obj, obj2), ">", 10) return {obj: "Hello", obj2: "World"} thedict = func() for key in thedict: print key.getlims()
OUTPUT:
Here, the objects are used as dictionary keys. This means that no variable names, at all, exist for these objects. Because they are the dictionary keys, meaning that they are the ones used to refer to the dictionary values, there is no real way to refer to them. For this reason, the only thing that the{"x*y > 10"} {"x*y > 10"}getlimsfunction can do is use the single-letter variables, passed when require was called, instead. Note that, when using a Cond object as a dictionary key, the main option of the object cannot be used to retrieve its corresponding value. In order to do so, the actual Cond object itself has to be passed as the dictionary key. - The second method is
clearlims(). As would be expected, this method will clear all the limitations for a LinkedCond object. The only thing that needs to be noted here, is thatclearlimswill not only clear the limitations for the object it is called on, but also for linked objects, that link the object to them. Consider the example where the product of two LinkedCond objects is 24 and one of them is smaller than 6.a = LinkedCond(range = 10) b = LinkedCond(range = 10) require("ab", (a, b), "=", 24) require("b", b, "<", 6) print(b.getlims()) a.clearlims() print(b.getlims())
OUTPUT:
In this case, when{'b < 6', 'a*b = 24'} {'b < 6'}clearlimswas called ona, the limitationa*b = 24was also removed fromb. However, the limitation ofbthat didn't includea,b < 6, did not change.
Credit
The inspiration for this project was solely taken from a lightning talk by Jason Orendorff, on Youtube.
His github profile can be found here.
This description on "Quantum Bogosort" also helped.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cond-1.2.5.tar.gz.
File metadata
- Download URL: cond-1.2.5.tar.gz
- Upload date:
- Size: 23.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.15.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/44.1.1 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/2.7.18
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dbf897b9125a203300a1aa5e9c8cd6a9c57c2bb9e719166a853437e332f1848f
|
|
| MD5 |
b46ca21589eb0f30c752936035c0b784
|
|
| BLAKE2b-256 |
450b58d7b8fef8f931b8b19d522058758492dc05fe93848bb82d164b9cb9eb0a
|
File details
Details for the file cond-1.2.5-py3-none-any.whl.
File metadata
- Download URL: cond-1.2.5-py3-none-any.whl
- Upload date:
- Size: 17.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.15.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/44.1.1 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/2.7.18
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2024bfca661263873b0293a6dccc91481169279cebc61acd35a6febf00813370
|
|
| MD5 |
c704882266236702e3e5566441c20989
|
|
| BLAKE2b-256 |
b012a56eb94daaee59255b48ead3ffc7750f50c0753dba607be07750ff6d7192
|